<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Stripe on 逍遥笔记</title><link>https://blog.7qiang.com/tags/stripe/</link><description>Recent content in Stripe on 逍遥笔记</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>逍遥笔记</copyright><lastBuildDate>Sun, 16 Aug 2026 22:00:00 +0800</lastBuildDate><atom:link href="https://blog.7qiang.com/tags/stripe/index.xml" rel="self" type="application/rss+xml"/><item><title>Stripe 协议支付自动化深度拆解：从 HAR 抓包到纯 API 全链路实现</title><link>https://blog.7qiang.com/posts/stripe-protocol-payment-automation-deep-dive-2026/</link><pubDate>Sun, 16 Aug 2026 22:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/stripe-protocol-payment-automation-deep-dive-2026/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post Stripe 协议支付自动化深度拆解：从 HAR 抓包到纯 API 全链路实现" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 免责声明&lt;/strong&gt;：本文仅供安全研究与技术学习。文中描述的技术手段仅用于分析 Stripe 支付协议的工作原理，帮助支付系统开发者理解安全边界。请勿将相关技术用于任何未授权的操作。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;做过 SaaS 订阅的人都知道，Stripe 是全球最主流的支付处理商。几乎所有主流 AI 平台——从对话类到图像类——都用 Stripe 处理信用卡支付。&lt;/p&gt;
&lt;p&gt;但很少有人去研究 Stripe Checkout 背后的协议细节。大多数人的认知停留在&amp;quot;调一下 API、跳个 Checkout 页面、信用卡扣完款就行了&amp;quot;。&lt;/p&gt;
&lt;p&gt;实际上，Stripe 的 Checkout Session 协议远比你想象的复杂。从 &lt;code&gt;checkout_ui_mode&lt;/code&gt; 的 &lt;code&gt;custom&lt;/code&gt; 与 &lt;code&gt;hosted&lt;/code&gt; 两种模式，到 &lt;code&gt;ConfirmationToken&lt;/code&gt; 的创建机制，再到 &lt;code&gt;PaymentIntent&lt;/code&gt; 的 &lt;code&gt;requires_action&lt;/code&gt; 状态下 3DS 验证挑战的处理——每一步都有大量的协议细节、反欺诈检测和时序要求。&lt;/p&gt;
&lt;p&gt;今天我把这些全拆开。从 HAR 抓包出发，逐层剖析 Stripe 协议支付的完整链路，并提供 5 套不同技术栈的实现源码。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="零方法论如何逆向-stripe-支付协议"&gt;零、方法论：如何逆向 Stripe 支付协议
&lt;/h2&gt;&lt;p&gt;在开始之前，先讲方法论。所有协议支付的起点都是 &lt;strong&gt;HAR 抓包&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="01-什么是-har-文件"&gt;0.1 什么是 HAR 文件
&lt;/h3&gt;&lt;p&gt;HAR（HTTP Archive）是浏览器开发者工具导出的完整 HTTP 交互记录。包含每个请求的 URL、Headers、Body、Response，以及精确的时序信息。&lt;/p&gt;
&lt;p&gt;打开 Chrome DevTools → Network → 完成一次真实支付 → 右键 → Save all as HAR with content。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这个 HAR 文件就是你的 ground truth&lt;/strong&gt;。所有协议支付的字段、值、顺序、编码方式，都以 HAR 为准。代码实现、第三方文档、安全审计报告——全部都可能过时或有误，只有 HAR 是当时浏览器真实发出的请求。&lt;/p&gt;
&lt;h3 id="02-har-分析流程"&gt;0.2 HAR 分析流程
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;筛选关键域名&lt;/strong&gt;：&lt;code&gt;api.stripe.com&lt;/code&gt;、&lt;code&gt;m.stripe.com&lt;/code&gt;、&lt;code&gt;checkout.stripe.com&lt;/code&gt;、平台 API 域名&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;按时间排序&lt;/strong&gt;：找出完整的请求链路&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对比成功和失败&lt;/strong&gt;：同一流程的成功 HAR 和失败 HAR，diff 出关键差异&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提取稳定字段&lt;/strong&gt;：跨多个 HAR 都一致的值可以硬编码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标记动态字段&lt;/strong&gt;：每次不同的值（Session ID、Token、时间戳）需要运行时获取&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="03-经过验证的-har-请求链路"&gt;0.3 经过验证的 HAR 请求链路
&lt;/h3&gt;&lt;p&gt;通过分析多个成功支付的 HAR 文件，提取出标准的浏览器 Checkout 请求序列：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. POST /api/v1/payments/checkout → 创建 session (oaics_xxx)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. GET stripe/v1/elements/sessions → 获取 Elements 配置 (deferred_intent)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. POST /api/v1/payments/checkout/taxes → 税务重算 (US免税地址可消除VAT)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. GET stripe/v1/elements/sessions → 更新后的 amount
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;5. POST stripe/v1/confirmation_tokens → 卡号 → ctoken_xxx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;6. POST /api/v1/payments/checkout/confirm → 平台确认 → pi_xxx client_secret
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;7. POST stripe/v1/payment_intents/{pi}/confirm → Stripe 侧扣款
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;8. GET /checkout/verify → redirect_status=succeeded
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每一步都有大量细节。下面逐一拆解。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一为什么要做协议支付"&gt;一、为什么要做&amp;quot;协议支付&amp;quot;
&lt;/h2&gt;&lt;p&gt;先搞清楚一个概念：什么是协议支付（Protocol Payment）？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;浏览器支付&lt;/strong&gt;：用户打开 Stripe Checkout 页面 → 手动填写信用卡号 → 点击支付 → 完成。这是正常流程。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;协议支付&lt;/strong&gt;：用纯 HTTP API 请求模拟浏览器的完整操作链路，包括 Stripe 的内部 API（不是官方 SDK 暴露的那些），直接在协议层完成支付，不需要浏览器。&lt;/p&gt;
&lt;p&gt;为什么要这么做？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自动化需求&lt;/strong&gt;：批量管理订阅时，手动操作不现实&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;性能需求&lt;/strong&gt;：浏览器（Puppeteer/Playwright）启动慢、占内存大、不稳定；协议支付 &amp;lt;5s 完成&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;环境隔离&lt;/strong&gt;：服务端运行，不需要 GUI 环境&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;学习价值&lt;/strong&gt;：深入理解支付系统的工作原理&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="二stripe-checkout-session-的两种模式"&gt;二、Stripe Checkout Session 的两种模式
&lt;/h2&gt;&lt;p&gt;Stripe Checkout 有两种 &lt;code&gt;checkout_ui_mode&lt;/code&gt;，这是整个协议支付的第一个分叉点：&lt;/p&gt;
&lt;h3 id="21-custom-模式-oaics_-前缀"&gt;2.1 Custom 模式 (&lt;code&gt;oaics_&lt;/code&gt; 前缀)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;POST /payments/checkout
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;checkout_ui_mode&amp;#34;: &amp;#34;custom&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;currency&amp;#34;: &amp;#34;PHP&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;plan_type&amp;#34;: &amp;#34;plus&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ {&amp;#34;checkout_session_id&amp;#34;: &amp;#34;oaics_xxx&amp;#34;, &amp;#34;stripe_publishable_key&amp;#34;: &amp;#34;pk_live_...&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Custom 模式返回的 Session ID 以 &lt;code&gt;oaics_&lt;/code&gt; 开头。这种模式下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;前端需要自己挂载 Stripe Elements&lt;/li&gt;
&lt;li&gt;通过 Stripe.js 创建 &lt;code&gt;ConfirmationToken&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;调用平台的 &lt;code&gt;confirm&lt;/code&gt; 接口完成支付&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;直接&lt;/strong&gt;在 Stripe API 上确认 PaymentIntent&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="22-hosted-模式-cs_live_-前缀"&gt;2.2 Hosted 模式 (&lt;code&gt;cs_live_&lt;/code&gt; 前缀)
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;POST /payments/checkout
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;checkout_ui_mode&amp;#34;: &amp;#34;hosted&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;currency&amp;#34;: &amp;#34;PHP&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;plan_type&amp;#34;: &amp;#34;plus&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ {&amp;#34;checkout_session_id&amp;#34;: &amp;#34;cs_live_xxx&amp;#34;, &amp;#34;url&amp;#34;: &amp;#34;https://checkout.stripe.com/...&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hosted 模式返回 &lt;code&gt;cs_live_&lt;/code&gt; 前缀的 Session。这种模式下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stripe 托管整个 Checkout 页面&lt;/li&gt;
&lt;li&gt;走 Stripe Payment Page 的内部协议&lt;/li&gt;
&lt;li&gt;流程更复杂，涉及 &lt;code&gt;ppage_&lt;/code&gt; 初始化、&lt;code&gt;elements&lt;/code&gt; Session、&lt;code&gt;inline_confirm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;需要额外处理平台侧的 &lt;code&gt;approve&lt;/code&gt; 操作&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两种模式的选择取决于平台的 billing 配置和你的需求。一般来说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果平台的计费货币与你的信用卡货币一致 → Custom 模式更简单&lt;/li&gt;
&lt;li&gt;如果存在货币不匹配（比如用美元卡付菲律宾比索订阅）→ 可能只有 Hosted 模式可用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="23-决定性参数checkout_ui_mode"&gt;2.3 决定性参数：&lt;code&gt;checkout_ui_mode&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;通过大量测试验证，&lt;strong&gt;决定返回 &lt;code&gt;oaics_&lt;/code&gt; 还是 &lt;code&gt;cs_live_&lt;/code&gt; 的唯一参数就是 &lt;code&gt;checkout_ui_mode&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 返回 oaics_ 的请求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;entry_point&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;all_plans_pricing_modal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plan_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;basic_plan&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;billing_details&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;country&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PH&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PHP&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;checkout_ui_mode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;custom&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 返回 cs_live_ 的请求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;plan_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;basic_plan&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;billing_details&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;country&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PH&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PHP&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;checkout_ui_mode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hosted&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cancel_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://platform.example.com/#pricing&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;locale&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;zh-CN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意差异：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;entry_point&lt;/code&gt;&lt;/strong&gt;：Custom 模式有，Hosted 模式没有&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cancel_url&lt;/code&gt;&lt;/strong&gt;：Hosted 模式需要（Stripe 托管页面的返回按钮用）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;locale&lt;/code&gt;&lt;/strong&gt;：Hosted 模式建议传（影响 Stripe 页面语言）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hosted 模式的响应多一个 &lt;code&gt;url&lt;/code&gt; 字段——这是 Stripe 托管页面的完整 URL，可以直接在浏览器中打开。&lt;/p&gt;
&lt;h3 id="24-_stripe_version-的版本差异"&gt;2.4 &lt;code&gt;_stripe_version&lt;/code&gt; 的版本差异
&lt;/h3&gt;&lt;p&gt;这是一个极其容易忽略的细节。两种模式使用&lt;strong&gt;不同的 &lt;code&gt;_stripe_version&lt;/code&gt;&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;模式&lt;/th&gt;
 &lt;th&gt;&lt;code&gt;_stripe_version&lt;/code&gt; 值&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Custom (oaics_)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;2025-03-31.basil&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Hosted (cs_live_)&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;2025-03-31.basil; checkout_server_update_beta=v1; checkout_manual_approval_preview=v1&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Hosted 模式的版本串后面带了两个 beta flag。如果在 cs_live_ 请求中用了 Custom 的短版本串，Stripe 会返回字段缺失或行为不一致。&lt;/p&gt;
&lt;h3 id="25-两种模式的完整字段对比"&gt;2.5 两种模式的完整字段对比
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;字段&lt;/th&gt;
 &lt;th&gt;Custom (oaics_)&lt;/th&gt;
 &lt;th&gt;Hosted (cs_live_)&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;确认端点&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;payment_intents/{pi}/confirm&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;payment_pages/{cs_live}/confirm&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;顶层 &lt;code&gt;source&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;elements&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;checkout&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;顶层 &lt;code&gt;version&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;2021&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;custom&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;selection_flow&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;merchant_specified&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;automatic&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;elements_session_client&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;有（8 个子字段）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;elements_options_client&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;有（2 个子字段）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;link_brand&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;link&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;checkout_config_id&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;有（两层不同值）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;hCaptcha 位置&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pmd[radar_options][hcaptcha_token]&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;passive_captcha_token&lt;/code&gt;（顶层）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;init_checksum&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;有&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;js_checksum&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;有&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;rv_timestamp&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;有&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;expected_amount&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;td&gt;有&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;这两套字段集完全不能混用&lt;/strong&gt;。这也是很多人协议支付做不通的原因——用了 oaics_ 的字段去调 cs_live_ 的接口，反过来也一样。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="三custom-模式协议流程详解"&gt;三、Custom 模式协议流程详解
&lt;/h2&gt;&lt;p&gt;Custom 模式是最干净的协议支付路径。完整流程 8 步：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌──────────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 1. 刷新 Session（获取最新 Token） │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 2. 查询账号信息（确认当前订阅状态） │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 3. 获取定价信息（币种+金额） │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 4. 创建 Checkout Session │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 5. 更新税务信息（Billing 地址） │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 6. 创建 ConfirmationToken（卡号→Token）│
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 7. 确认支付（Confirm Checkout） │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 8. 确认 PaymentIntent（Stripe 侧） │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└──────────────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="31-创建-confirmationtoken"&gt;3.1 创建 ConfirmationToken
&lt;/h3&gt;&lt;p&gt;这是协议支付最核心的一步。浏览器里，Stripe.js 会在 iframe 中收集卡号，然后调用 Stripe 的内部 API 创建一个 &lt;code&gt;ConfirmationToken&lt;/code&gt;（&lt;code&gt;ctoken_&lt;/code&gt; 前缀）。&lt;/p&gt;
&lt;p&gt;协议支付需要直接模拟这个请求：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/confirmation_tokens
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Content-Type: application/x-www-form-urlencoded
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;type=card
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[type]=card
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[card][number]=4242424242424242
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[card][exp_month]=12
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[card][exp_year]=2029
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[card][cvc]=123
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[billing_details][name]=John Doe
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[billing_details][address][country]=US
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;payment_method_data[billing_details][address][postal_code]=97201
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;key=pk_live_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&amp;amp;_stripe_version=2025-03-31.basil
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;几个关键细节：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;key&lt;/code&gt; 必须是平台的 Stripe Publishable Key&lt;/strong&gt;，不是你自己的&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;_stripe_version&lt;/code&gt; 必须匹配&lt;/strong&gt;当前 Stripe.js 的 API 版本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;需要传 Stripe.js 的指纹参数&lt;/strong&gt;：&lt;code&gt;guid&lt;/code&gt;、&lt;code&gt;muid&lt;/code&gt;、&lt;code&gt;sid&lt;/code&gt;、&lt;code&gt;time_on_page&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Referer 必须是平台域名&lt;/strong&gt;，Stripe 会校验&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id="卡号格式细节"&gt;卡号格式细节
&lt;/h4&gt;&lt;p&gt;HAR 验证发现，浏览器发送的卡号&lt;strong&gt;带空格&lt;/strong&gt;（&lt;code&gt;4004 1641 0185 9397&lt;/code&gt;），而不是纯数字。不过 Stripe 接受两种格式。&lt;/p&gt;
&lt;h4 id="guidmuidsid-格式"&gt;guid/muid/sid 格式
&lt;/h4&gt;&lt;p&gt;这三个指纹 ID 的格式比很多人以为的更复杂。HAR 验证的正确格式是 &lt;strong&gt;42 字符&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;guid = randomUUID() + randomHex(6)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx + 6位hex
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; = 36字符UUID + 6字符 = 42字符
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;例如：&lt;code&gt;a1b2c3d4-e5f6-4789-abcd-ef0123456789abc123&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;有些实现用 32 位纯 hex 也能工作，但 HAR 中浏览器发送的是 42 字符格式。&lt;/p&gt;
&lt;h4 id="attribution-元数据两层结构"&gt;Attribution 元数据（两层结构）
&lt;/h4&gt;&lt;p&gt;&lt;code&gt;confirmation_tokens&lt;/code&gt; 请求中有一组 &lt;code&gt;client_attribution_metadata&lt;/code&gt; 字段，这些是 Stripe 用来追踪支付集成来源的元数据：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;payment_user_agent = stripe.js/...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;client_session_id = &amp;lt;UUID&amp;gt; # 每 session 唯一
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;merchant_integration_source = elements # ← oaics_ 固定 &amp;#34;elements&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;merchant_integration_version = 2021
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;merchant_integration_subtype = payment-element
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;payment_intent_creation_flow = deferred
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;payment_method_selection_flow = merchant_specified # ← oaics_ 固定
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;additional_elements = [expressCheckout, payment, address]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些字段在 &lt;code&gt;confirmation_tokens&lt;/code&gt; 请求中出现两次——一次在 &lt;code&gt;payment_method_data&lt;/code&gt; 内部，一次在顶层。&lt;strong&gt;两层的值大部分相同，但 &lt;code&gt;merchant_integration_source&lt;/code&gt; 在 confirm 请求中变成 &lt;code&gt;l1&lt;/code&gt;&lt;/strong&gt;。这个细节从 HAR 中才能发现。&lt;/p&gt;
&lt;h4 id="请求头"&gt;请求头
&lt;/h4&gt;&lt;p&gt;HAR 中 Stripe API 请求的完整请求头：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Accept: application/json
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Content-Type: application/x-www-form-urlencoded
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Origin: https://js.stripe.com
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Referer: https://js.stripe.com/
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Sec-CH-UA: &amp;#34;Not=A?Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;151&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Sec-CH-UA-Mobile: ?0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Sec-CH-UA-Platform: &amp;#34;Windows&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Sec-Fetch-Site: same-site
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Sec-Fetch-Mode: cors
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Sec-Fetch-Dest: empty
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Accept-Language: en-US,en;q=0.9
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意 &lt;code&gt;Accept-Language&lt;/code&gt; 必须存在。这是纯协议支付跑通的关键 bug 之一——缺少这个 header 会导致 Stripe 返回非预期的响应格式。&lt;/p&gt;
&lt;p&gt;返回值：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ctoken_1xxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_method_preview&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;brand&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;visa&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;last4&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;4242&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="32-确认-checkout"&gt;3.2 确认 Checkout
&lt;/h3&gt;&lt;p&gt;拿到 &lt;code&gt;ConfirmationToken&lt;/code&gt; 后，调用平台的 confirm 接口：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST /api/v1/payments/checkout/confirm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;checkout_session_id&amp;#34;: &amp;#34;oaics_xxx&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;confirmation_token&amp;#34;: &amp;#34;ctoken_1xxx&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这一步平台会拿着你的 token 去 Stripe 创建 PaymentIntent 并尝试扣款。&lt;/p&gt;
&lt;p&gt;如果一切顺利（卡余额充足、无 3DS 要求），PaymentIntent 直接变成 &lt;code&gt;succeeded&lt;/code&gt;，订阅激活。&lt;/p&gt;
&lt;p&gt;如果遇到 3DS 验证，流程会更复杂——我们在第五节讲。&lt;/p&gt;
&lt;h3 id="33-elements-session--获取支付配置"&gt;3.3 Elements Session — 获取支付配置
&lt;/h3&gt;&lt;p&gt;在创建 ConfirmationToken 之前，需要先调用 Stripe 的 &lt;code&gt;elements/sessions&lt;/code&gt; API 获取支付配置信息。这一步在 HAR 中被调用&lt;strong&gt;两次&lt;/strong&gt;——一次在税务更新前，一次在税务更新后。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;GET https://api.stripe.com/v1/elements/sessions?deferred_intent[mode]=subscription
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;amp;deferred_intent[amount]=98214
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;amp;deferred_intent[currency]=php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;amp;deferred_intent[setup_future_usage]=off_session
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;amp;deferred_intent[payment_method_types][0]=card
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;amp;type=deferred_intent
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;amp;key=pk_live_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;amp;_stripe_version=2025-03-31.basil
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键返回值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;elements_session_id&lt;/code&gt;&lt;/strong&gt;：&lt;code&gt;elements_session_xxx&lt;/code&gt;，后续 confirm 请求需要&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;customer_session_client_secret&lt;/code&gt;&lt;/strong&gt;：&lt;code&gt;cuss_xxx&lt;/code&gt;，客户会话密钥&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;payment_method_configuration&lt;/code&gt;&lt;/strong&gt;：&lt;code&gt;pmc_xxx&lt;/code&gt;，支付方式配置 ID&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="34-税务地址优化--us-免税地址消除-vat"&gt;3.4 税务地址优化 — US 免税地址消除 VAT
&lt;/h3&gt;&lt;p&gt;这是一个实际操作中极其重要的技巧。&lt;/p&gt;
&lt;p&gt;很多 SaaS 平台默认按账号注册地收税。比如菲律宾区的订阅，标价 ₱982.14 但实际会加 12% VAT 变成 ₱1,100+。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：在 &lt;code&gt;taxes&lt;/code&gt; API 调用中使用美国免税州地址。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST /api/v1/payments/checkout/taxes
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;checkout_session_id&amp;#34;: &amp;#34;oaics_xxx&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;billing_details&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;name&amp;#34;: &amp;#34;John Doe&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;address&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;country&amp;#34;: &amp;#34;US&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;state&amp;#34;: &amp;#34;OR&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;city&amp;#34;: &amp;#34;Portland&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;postal_code&amp;#34;: &amp;#34;97201&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;line1&amp;#34;: &amp;#34;123 Main St&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;美国有 5 个无销售税的州：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Oregon (OR)&lt;/strong&gt; — 无销售税&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Montana (MT)&lt;/strong&gt; — 无销售税&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Delaware (DE)&lt;/strong&gt; — 无销售税&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New Hampshire (NH)&lt;/strong&gt; — 无销售税&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alaska (AK)&lt;/strong&gt; — 大部分地区无销售税&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;HAR 验证：使用 US 地址后 tax=0，最终金额保持 ₱982.14。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重要&lt;/strong&gt;：&lt;code&gt;taxes&lt;/code&gt; 和后续 &lt;code&gt;confirmation_tokens&lt;/code&gt; 中的地址必须一致。同一个 session 内选择一个地址后全程复用。&lt;/p&gt;
&lt;h3 id="35-paymentintent-确认--stripe-侧扣款"&gt;3.5 PaymentIntent 确认 — Stripe 侧扣款
&lt;/h3&gt;&lt;p&gt;平台的 &lt;code&gt;/confirm&lt;/code&gt; 返回 &lt;code&gt;client_secret&lt;/code&gt;（格式：&lt;code&gt;pi_xxx_secret_xxx&lt;/code&gt;）后，需要调用 Stripe 的 &lt;code&gt;payment_intents/confirm&lt;/code&gt; 完成实际扣款：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/payment_intents/{pi_id}/confirm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;return_url = https://platform.example.com/checkout/verify?stripe_session_id=oaics_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;confirmation_token = ctoken_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;key = pk_live_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;_stripe_version = 2025-03-31.basil
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;client_secret = pi_xxx_secret_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;client_attribution_metadata[client_session_id] = &amp;lt;UUID&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;client_attribution_metadata[merchant_integration_source] = l1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意这里 &lt;code&gt;merchant_integration_source&lt;/code&gt; 变成了 &lt;code&gt;l1&lt;/code&gt;，不再是 &lt;code&gt;elements&lt;/code&gt;。&lt;/p&gt;
&lt;h4 id="pi-拒卡重试"&gt;PI 拒卡重试
&lt;/h4&gt;&lt;p&gt;HAR 验证了一个重要特性：&lt;strong&gt;同一个 PaymentIntent 被拒卡后可以用新的 ConfirmationToken 重试&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一张卡 → &lt;code&gt;generic_decline&lt;/code&gt; → PI 状态 &lt;code&gt;requires_payment_method&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;创建新的 &lt;code&gt;confirmation_token&lt;/code&gt;（用另一张卡）&lt;/li&gt;
&lt;li&gt;再次调用 &lt;code&gt;payment_intents/confirm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;第二张卡 → &lt;code&gt;succeeded&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;这意味着拒卡不需要重建整个 Checkout Session&lt;/strong&gt;，只需要换卡重建 token。&lt;/p&gt;
&lt;h3 id="36-cookie-隔离架构"&gt;3.6 Cookie 隔离架构
&lt;/h3&gt;&lt;p&gt;协议支付需要严格的 Cookie 隔离。一个常见的 bug 是平台的 Cookie 被发送到 Stripe API，或反过来。&lt;/p&gt;
&lt;p&gt;最佳实践：使用两个独立的 HTTP 客户端：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 平台客户端 — 带 Cookie（session token）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;platform_http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookies_enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;platform_http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;__Secure-platform-auth&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Stripe 客户端 — 不带 Cookie&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;stripe_http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookies_enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;绝不&lt;/strong&gt;让 Stripe 客户端发送平台 Cookie，也不要让平台客户端发送 Stripe 的 Cookie。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四hosted-模式协议流程详解"&gt;四、Hosted 模式协议流程详解
&lt;/h2&gt;&lt;p&gt;Hosted 模式（&lt;code&gt;cs_live_&lt;/code&gt;）的流程更复杂，因为需要模拟 Stripe Payment Page 的完整内部协议。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌────────────────────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 1. 创建 Checkout (hosted) → cs_live_ session │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 2. 更新税务 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 3. 分配信用卡 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 4. 初始化 Payment Page → ppage_ token │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 5. 获取 Elements Session │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 6. 创建 ConfirmationToken │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 7. 确认 Checkout (内部 confirm) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 8. 平台 Approve 操作 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 9. 轮询 PaymentIntent 状态 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 10. 处理 3DS Challenge（如果需要） │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 11. 验证最终状态 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└────────────────────────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="41-payment-page-初始化"&gt;4.1 Payment Page 初始化
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;GET https://checkout.stripe.com/api/payment-page/{cs_live_xxx}/init
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;→ {&amp;#34;ppage_token&amp;#34;: &amp;#34;ppage_xxx&amp;#34;, &amp;#34;merchant_name&amp;#34;: &amp;#34;...&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这是 Hosted 模式独有的。返回一个 &lt;code&gt;ppage_&lt;/code&gt; token，后续所有 Stripe Payment Page API 都需要这个 token。&lt;/p&gt;
&lt;h3 id="42-elements-sessionhosted-模式特殊字段"&gt;4.2 Elements Session（Hosted 模式特殊字段）
&lt;/h3&gt;&lt;p&gt;Hosted 模式的 &lt;code&gt;elements/sessions&lt;/code&gt; 请求比 Custom 模式多出很多字段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/elements/sessions
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;deferred_intent[mode] = subscription
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;deferred_intent[amount] = 98214
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;deferred_intent[currency] = php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;deferred_intent[setup_future_usage] = off_session
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;deferred_intent[payment_method_types][0] = card
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;deferred_intent[payment_method_configuration][id] = pmc_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;currency = php
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;elements_init_source = custom_checkout
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;referrer_host = platform.example.com
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;stripe_js_id = &amp;lt;UUID&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;locale = zh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;type = deferred_intent
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;checkout_session_id = cs_live_xxx
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;_stripe_version = 2025-03-31.basil; checkout_server_update_beta=v1; ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键差异：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要传 &lt;code&gt;checkout_session_id&lt;/code&gt;（Custom 模式不需要）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;elements_init_source&lt;/code&gt; 是 &lt;code&gt;custom_checkout&lt;/code&gt;（不是 &lt;code&gt;elements&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;需要传 &lt;code&gt;referrer_host&lt;/code&gt;、&lt;code&gt;stripe_js_id&lt;/code&gt;、&lt;code&gt;locale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;使用&lt;strong&gt;长版本&lt;/strong&gt;的 &lt;code&gt;_stripe_version&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;返回值中有一个重要字段 &lt;code&gt;passive_captcha.rqdata&lt;/code&gt;——这是 hCaptcha Enterprise 的 &lt;code&gt;rqdata&lt;/code&gt;，后续 confirm 如果触发 challenge 需要用到。&lt;/p&gt;
&lt;h3 id="43-hosted-模式的-confirm-请求"&gt;4.3 Hosted 模式的 Confirm 请求
&lt;/h3&gt;&lt;p&gt;cs_live_ 的 confirm 走的是 &lt;code&gt;payment_pages/confirm&lt;/code&gt;，不是 &lt;code&gt;payment_intents/confirm&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/payment_pages/{cs_live_xxx}/confirm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个请求的 body 字段比 Custom 模式多很多：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[client_betas][0] = custom_checkout_server_updates_1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[client_betas][1] = custom_checkout_manual_approval_1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[elements_init_source] = custom_checkout
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[referrer_host] = platform.example.com
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[session_id] = elements_session_xxx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[stripe_js_id] = &amp;lt;UUID&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[locale] = zh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_session_client[is_aggregation_expected] = false
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_options_client[saved_payment_method][enable_save] = auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;elements_options_client[saved_payment_method][enable_redisplay] = auto
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;init_checksum = &amp;lt;checksum&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;js_checksum = &amp;lt;checksum&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rv_timestamp = &amp;lt;timestamp&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;expected_amount = 98214
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;expected_payment_method_type = card
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;version = &amp;lt;build_hash&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;js_checksum&lt;/code&gt; 和 &lt;code&gt;rv_timestamp&lt;/code&gt; 是从 Stripe.js 部署状态动态提取的，&lt;code&gt;init_checksum&lt;/code&gt; 来自 init 响应。&lt;/p&gt;
&lt;h3 id="44-approve-机制与-sentinel-token"&gt;4.4 Approve 机制与 Sentinel Token
&lt;/h3&gt;&lt;p&gt;Hosted 模式下，&lt;code&gt;payment_pages/confirm&lt;/code&gt; 成功后，状态不会直接变成 &lt;code&gt;succeeded&lt;/code&gt;，而是进入 &lt;code&gt;requires_approval&lt;/code&gt;（HAR 中 confirm 返回 &lt;code&gt;status=open&lt;/code&gt;）。这时需要调用平台的 approve 接口：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST /api/v1/payments/checkout/approve
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Authorization: Bearer {access_token}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;X-Vendor-Challenge-Token: {sentinel_token}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 id="sentinel-token-结构"&gt;Sentinel Token 结构
&lt;/h4&gt;&lt;p&gt;这里的 &lt;code&gt;challenge_token&lt;/code&gt; 是一个编码后的 JSON 对象，内部包含多个通过算力证明（PoW）和 Turnstile 验证生成的字段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;p&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gAAAAAB...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 主负载（加密后的 PoW + session 信息）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// enforcement_token（基于 FNV-1a 的算力证明结果）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;UUID&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 一次性标识
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;flow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;platform_checkout&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 标记来源流程
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;t&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;turnstile_dx_token&amp;gt;&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;// 4000+ 字符的 Turnstile 验证 token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;t&lt;/code&gt; 字段是核心&lt;/strong&gt;——如果为空字符串，approve 立即返回 &lt;code&gt;blocked&lt;/code&gt;。它来自 Cloudflare Turnstile 的隐式挑战验证（不是肉眼可见的验证码）。&lt;/p&gt;
&lt;h4 id="sentinel-token-生成流程"&gt;Sentinel Token 生成流程
&lt;/h4&gt;&lt;p&gt;完整的 Sentinel 生成需要两个组件协作：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. 生成 requirements_token（本地 seed + difficulty &amp;#34;0&amp;#34;）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. POST sentinel.platform.com/backend-api/sentinel/req
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 返回 {turnstile: {dx: &amp;#34;...&amp;#34;}, ...}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. 用 server seed 生成 enforcement_token（FNV-1a 算力证明）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4. 在 Node.js VM 中执行 Turnstile SDK（需要 jsdom 模拟 DOM 环境）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 返回 turnstile token（~4388 字符）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;5. 组装 JSON → Base64 → 设为请求头
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PoW 使用 &lt;strong&gt;FNV-1a&lt;/strong&gt; 哈希算法，difficulty 对 checkout 流程通常是 “0”（即几乎不需要算力）&lt;/li&gt;
&lt;li&gt;Turnstile SDK 必须在&lt;strong&gt;真实的 DOM 环境&lt;/strong&gt;中执行——简单的字符串拼接无法通过验证&lt;/li&gt;
&lt;li&gt;实际生产中需要 Node.js + jsdom 来执行官方 Turnstile VM&lt;/li&gt;
&lt;li&gt;SDK 代码需要定期更新（随平台版本迭代）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="approve-的正确时机"&gt;Approve 的正确时机
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;关键发现&lt;/strong&gt;：Approve 不应该主动调用。正确的流程是 &lt;strong&gt;fall-through&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;confirm → status=open, requires_approval
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 等待 Stripe 回调
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 平台自动完成 approve（不需要客户端调）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; poll payment_page → PI 出现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 可能 requires_action → verify_challenge
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; poll 到 succeeded → 完成
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但某些边缘情况下（平台回调未触发），客户端需要手动 approve。此时 Sentinel token 是必须的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Approve 是&lt;strong&gt;一次性操作&lt;/strong&gt;，同一 session 不可重复&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;t&lt;/code&gt; 字段为空或格式错误 → 返回 &lt;code&gt;blocked&lt;/code&gt; → session 作废&lt;/li&gt;
&lt;li&gt;成功的 approve 返回 &lt;code&gt;{&amp;quot;result&amp;quot;:&amp;quot;approved&amp;quot;}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="五3ds-验证挑战"&gt;五、3DS 验证挑战
&lt;/h2&gt;&lt;p&gt;当 Stripe 判断交易需要额外验证时，PaymentIntent 会进入 &lt;code&gt;requires_action&lt;/code&gt; 状态，附带 3DS challenge 信息。&lt;/p&gt;
&lt;h3 id="51-challenge-类型"&gt;5.1 Challenge 类型
&lt;/h3&gt;&lt;p&gt;Stripe 使用 hCaptcha Enterprise 作为 3DS 验证手段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;requires_action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;next_action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;verify_with_challenge&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;verify_with_challenge&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://challenges.stripe.com/...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;challenge_type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hcaptcha&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="52-verify_challenge-完整请求格式"&gt;5.2 verify_challenge 完整请求格式
&lt;/h3&gt;&lt;p&gt;HAR 中验证的 &lt;code&gt;verify_challenge&lt;/code&gt; 完整请求：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/payment_intents/{pi_id}/verify_challenge
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;captcha_response = {hcaptcha_token} # P0开头的长字符串
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;captcha_vendor_name = hcaptcha # 固定值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;captcha_sitekey = xxxxxxxx-xxxx-xxxx # Stripe 的 hCaptcha sitekey
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;expected_amount = 98214 # 交易金额
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;expected_currency = php # 币种
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;key = pk_live_xxx # Publishable Key
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;_stripe_version = 2025-03-31.basil; checkout_server_update_beta=v1; ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;client_secret = pi_xxx_secret_xxx # PI 的 client_secret
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;字段名是 &lt;code&gt;captcha_response&lt;/code&gt; 而不是 &lt;code&gt;challenge_response&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;需要同时传 &lt;code&gt;captcha_vendor_name&lt;/code&gt; 和 &lt;code&gt;captcha_sitekey&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;需要传 &lt;code&gt;expected_amount&lt;/code&gt;/&lt;code&gt;expected_currency&lt;/code&gt; 做一致性校验&lt;/li&gt;
&lt;li&gt;使用&lt;strong&gt;长版本&lt;/strong&gt; &lt;code&gt;_stripe_version&lt;/code&gt;（与 Hosted 模式一致）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="53-hcaptcha-enterprise-的特殊性"&gt;5.3 hCaptcha Enterprise 的特殊性
&lt;/h3&gt;&lt;p&gt;Stripe 使用的不是普通 hCaptcha，而是 &lt;strong&gt;hCaptcha Enterprise&lt;/strong&gt;。区别：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;有 &lt;code&gt;rqdata&lt;/code&gt; 参数&lt;/strong&gt;：普通 hCaptcha 不需要 rqdata，Enterprise 必须传&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sitekey 是 Stripe 专有的&lt;/strong&gt;：从 &lt;code&gt;elements/sessions&lt;/code&gt; 响应的 &lt;code&gt;passive_captcha&lt;/code&gt; 字段中提取&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;invisible 模式&lt;/strong&gt;：浏览器里看不到验证码图片，但后台在做风控评估&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 从 elements_session 响应中提取&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rqdata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elements_session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;passive_captcha&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rqdata&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sitekey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;elements_session&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;passive_captcha&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sitekey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="54-时效性问题"&gt;5.4 时效性问题
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;hCaptcha token 有时效性&lt;/strong&gt;——浏览器里 ~4 秒解出即有效，但远程打码服务可能需要 30-120 秒。如果超时：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;verify_challenge&lt;/code&gt; 返回 &lt;code&gt;payment_intent_authentication_failure&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;但 PI 状态仍然是 &lt;code&gt;requires_action&lt;/code&gt;——&lt;strong&gt;可以重新 verify_challenge&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解决方案分级：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;最优&lt;/strong&gt;：本地 headless 浏览器解 hCaptcha（&amp;lt;5s，成功率 95%+）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;次优&lt;/strong&gt;：支持 Enterprise rqdata 的快速打码 API（如 YesCaptcha，~15s）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;兜底&lt;/strong&gt;：如果 PI 已经进入 &lt;code&gt;requires_action&lt;/code&gt;，可以循环重试 verify_challenge（每次用新 captcha token）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="55-3ds-vs-hcaptcha-的区别"&gt;5.5 3DS vs hCaptcha 的区别
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;重要澄清&lt;/strong&gt;：Stripe 的 &lt;code&gt;verify_with_challenge&lt;/code&gt; 并非传统银行 3DS 验证（3D Secure）。它是 Stripe 自有的风控挑战，使用 hCaptcha 作为人机验证手段。&lt;/p&gt;
&lt;p&gt;真正的银行 3DS（如 Visa Secure）会打开银行页面要求输入 OTP/密码，这个在纯协议支付中&lt;strong&gt;无法绕过&lt;/strong&gt;——但使用虚拟信用卡（VCC）通常不会触发银行 3DS，只会触发 Stripe 自身的 hCaptcha challenge。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="六tls-指纹对抗"&gt;六、TLS 指纹对抗
&lt;/h2&gt;&lt;p&gt;纯 HTTP 请求最大的挑战不是协议逻辑，而是 &lt;strong&gt;TLS 指纹&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;Stripe 和 Cloudflare 都会检查 TLS Client Hello 的指纹（JA3/JA4），如果发现你的 TLS 握手特征不像浏览器，直接返回 403/429。&lt;/p&gt;
&lt;h3 id="61-指纹伪装"&gt;6.1 指纹伪装
&lt;/h3&gt;&lt;p&gt;Python 项目使用 &lt;code&gt;curl_cffi&lt;/code&gt; 库，支持模拟多种浏览器的 TLS 指纹：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PROFILES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;chrome131&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mozilla/5.0 ... Chrome/131.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;chrome124&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mozilla/5.0 ... Chrome/124.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;safari18&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mozilla/5.0 ... Safari/605.1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;firefox117&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mozilla/5.0 ... Firefox/117.0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Node.js 项目使用 &lt;code&gt;CycleTLS&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;initCycleTLS&lt;/span&gt; &lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;cycletls&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cycleTLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;initCycleTLS&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cycleTLS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;ja3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;771,4865-4866-4867...&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Mozilla/5.0 ...&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="62-指纹轮换策略"&gt;6.2 指纹轮换策略
&lt;/h3&gt;&lt;p&gt;当遇到 403/429 时，自动切换到下一个 TLS 指纹配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TLSFingerprintManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rotate_on_failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exclude_current&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pick_next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最多重试 3 次，每次使用不同的浏览器指纹。&lt;/p&gt;
&lt;h3 id="63-tls-指纹与-user-agent-一致性"&gt;6.3 TLS 指纹与 User-Agent 一致性
&lt;/h3&gt;&lt;p&gt;一个常见错误是 TLS 指纹和 User-Agent 不匹配。比如用 Chrome 131 的 TLS 指纹配置但 User-Agent 写的是 Chrome 124，Cloudflare 的 bot detection 会立即发现这种不一致。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;正确做法&lt;/strong&gt;：TLS profile 和 UA 必须成对配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PROFILES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;tls&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;chrome131&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ua&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/131.0.0.0 Safari/537.36&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sec_ch_ua&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#34;Not=A?Brand&amp;#34;;v=&amp;#34;99&amp;#34;, &amp;#34;Chromium&amp;#34;;v=&amp;#34;131&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;tls&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;safari18&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;ua&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) ... Safari/605.1.15&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sec_ch_ua&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt; &lt;span class="c1"&gt;# Safari 不发 sec-ch-ua&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="64-http-协议版本"&gt;6.4 HTTP 协议版本
&lt;/h3&gt;&lt;p&gt;另一个细节：Stripe 的 API 请求在浏览器中走 &lt;strong&gt;HTTP/2&lt;/strong&gt;，但 &lt;code&gt;payment_intents/confirm&lt;/code&gt; 在某些实现中用 HTTP/1.1 反而更稳定。&lt;/p&gt;
&lt;p&gt;HAR 中观察到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;confirmation_tokens&lt;/code&gt;：HTTP/2&lt;/li&gt;
&lt;li&gt;&lt;code&gt;payment_intents/confirm&lt;/code&gt;：可以 HTTP/1.1&lt;/li&gt;
&lt;li&gt;&lt;code&gt;payment_pages/confirm&lt;/code&gt;：HTTP/2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用 &lt;code&gt;curl_cffi&lt;/code&gt; 时默认走 HTTP/2（如果服务端支持），CycleTLS 需要在选项中显式指定。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七stripejs-指纹参数"&gt;七、Stripe.js 指纹参数
&lt;/h2&gt;&lt;p&gt;Stripe 的 &lt;code&gt;confirmation_tokens&lt;/code&gt; 和 &lt;code&gt;payment_intents/confirm&lt;/code&gt; 接口都需要一组指纹参数。这些参数由 Stripe.js 在前端收集，协议支付需要正确模拟：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;参数&lt;/th&gt;
 &lt;th&gt;含义&lt;/th&gt;
 &lt;th&gt;生成方式&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;guid&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;全局唯一 ID&lt;/td&gt;
 &lt;td&gt;UUID + 6位 hex = 42 字符（见 3.1 节）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;muid&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;设备 ID&lt;/td&gt;
 &lt;td&gt;同 guid 格式，42 字符&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;sid&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;会话 ID&lt;/td&gt;
 &lt;td&gt;同 guid 格式，42 字符&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;time_on_page&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;页面停留时间&lt;/td&gt;
 &lt;td&gt;随机 8-30 秒（模拟真实用户）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Publishable Key&lt;/td&gt;
 &lt;td&gt;从 Checkout Session 获取&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;_stripe_version&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;API 版本&lt;/td&gt;
 &lt;td&gt;从 Stripe.js 的 basil 部署中提取&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="71-mstripecom6--指纹注册"&gt;7.1 m.stripe.com/6 — 指纹注册
&lt;/h3&gt;&lt;p&gt;在所有业务请求之前，Stripe.js 会向 &lt;code&gt;m.stripe.com/6&lt;/code&gt; 发送 4 次 POST 请求注册设备指纹。HAR 中的完整调用链：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;POST https://m.stripe.com/6 (×4)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每次请求的 body 是 &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;，包含：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;v2 = 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;id = &amp;lt;stripe_js_id&amp;gt; # 与 confirmation_tokens 中的 guid 不同
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;tag = &amp;lt;event_tag&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;src = js
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;a = &amp;lt;JSON编码的分析数据&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;tag&lt;/code&gt; 的值依次为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;stripejs-init-started&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stripejs-init-complete&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;adyen-component-loaded&lt;/code&gt;（如果有 Adyen）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;payment-element-mounted&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;a&lt;/code&gt; 字段包含浏览器环境的详细指纹数据（屏幕尺寸、时区、语言、plugin 列表等）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;实际影响&lt;/strong&gt;：不发这些请求也能支付成功，但长期使用同一个 &lt;code&gt;guid&lt;/code&gt;/&lt;code&gt;muid&lt;/code&gt; 而从未注册过指纹可能触发 Stripe Radar 的异常检测。建议至少发一次 &lt;code&gt;stripejs-init-complete&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="72-rstripecomb--遥测上报"&gt;7.2 r.stripe.com/b — 遥测上报
&lt;/h3&gt;&lt;p&gt;Stripe.js 还会持续向 &lt;code&gt;r.stripe.com/b&lt;/code&gt; 发送遥测数据：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;POST&lt;/span&gt; &lt;span class="n"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;plain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个请求的 payload 是 JSON（不是 form-urlencoded），包含支付流程中的各种事件指标（加载时间、渲染时间、错误数）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;measurement&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event_name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;paymentElement.mount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;metrics&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nt"&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;342&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;client_session_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;UUID&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;publish_key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pk_live_xxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;实际影响&lt;/strong&gt;：纯可选。不发不影响支付成功。但如果你的实现在生产中运行，偶尔发几次遥测可以让你的流量看起来更像真实浏览器。&lt;/p&gt;
&lt;h3 id="73-动态提取-stripejs-版本"&gt;7.3 动态提取 Stripe.js 版本
&lt;/h3&gt;&lt;p&gt;Stripe.js 的版本（&lt;code&gt;_stripe_version&lt;/code&gt;）和构建哈希（&lt;code&gt;rv&lt;/code&gt;/&lt;code&gt;sv&lt;/code&gt;）不是固定的，需要从 Stripe 的部署状态接口动态提取：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 获取当前 Stripe.js 部署信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://js.stripe.com/deploy_status_henson.json&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;basil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// basil.rv = &amp;#34;2025-03-31&amp;#34; → _stripe_version = &amp;#34;2025-03-31.basil&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Custom 模式只需要基础版本：&lt;code&gt;2025-03-31.basil&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Hosted 模式需要追加 beta flags：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2025-03-31.basil; checkout_server_update_beta=v1; custom_checkout_beta=v1; ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这些 beta flags 从 &lt;code&gt;deploy_status_henson.json&lt;/code&gt; 的 metadata 或 checkout 页面的初始化脚本中提取。Stripe 大约每 1-2 周更新一次部署版本。&lt;/p&gt;
&lt;h3 id="74-js_checksum-和-rv_timestamp"&gt;7.4 js_checksum 和 rv_timestamp
&lt;/h3&gt;&lt;p&gt;Hosted 模式的 &lt;code&gt;payment_pages/confirm&lt;/code&gt; 需要两个动态校验值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;js_checksum&lt;/code&gt;&lt;/strong&gt;：当前 Stripe.js bundle 的 SHA 校验和，从 deploy status 提取&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;rv_timestamp&lt;/code&gt;&lt;/strong&gt;：当前 Stripe.js 的部署时间戳（Unix 毫秒），也从 deploy status 提取&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两个值随 Stripe.js 版本更新而变化。如果使用过期的 checksum/timestamp，confirm 可能会被静默拒绝。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;deploy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://js.stripe.com/deploy_status_henson.json&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;js_checksum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;basil&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;js_checksum&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;rv_timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;basil&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;rv_timestamp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="八代理池架构"&gt;八、代理池架构
&lt;/h2&gt;&lt;p&gt;协议支付的另一个核心组件是代理池。Stripe 和平台都会检查请求的源 IP，用数据中心 IP 直连容易被标记。&lt;/p&gt;
&lt;h3 id="81-双代理池"&gt;8.1 双代理池
&lt;/h3&gt;&lt;p&gt;最佳实践是使用两个独立的代理池：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;平台代理池&lt;/strong&gt;：用于访问 SaaS 平台的 API（需要与账号注册地匹配）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stripe 代理池&lt;/strong&gt;：用于访问 Stripe API（需要住宅 IP，避免被 Radar 标记）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="82-代理健康检查"&gt;8.2 代理健康检查
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProxyVerifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://ipapi.co/json/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proxies&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;proxy_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;country_code&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;expected_country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;ProxyCountryMismatchError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expected_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="83-出口-ip-一致性"&gt;8.3 出口 IP 一致性
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;关键规则&lt;/strong&gt;：创建 Checkout Session 和确认支付时，必须使用&lt;strong&gt;同一个出口 IP&lt;/strong&gt;。Stripe 会比对两次请求的 IP，如果不一致会拒绝交易。&lt;/p&gt;
&lt;p&gt;这意味着整个支付流程中的所有请求（从创建 session 到 confirm）必须绑定同一个代理出口。如果代理池做了负载均衡，需要在支付流程中&amp;quot;钉住&amp;quot;一个代理节点。&lt;/p&gt;
&lt;h3 id="84-代理类型选择"&gt;8.4 代理类型选择
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;代理类型&lt;/th&gt;
 &lt;th&gt;适用场景&lt;/th&gt;
 &lt;th&gt;风险&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;住宅代理（Residential）&lt;/td&gt;
 &lt;td&gt;Stripe API / 平台 API&lt;/td&gt;
 &lt;td&gt;最安全，IP 信誉高&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ISP 代理（Static Residential）&lt;/td&gt;
 &lt;td&gt;长期固定使用&lt;/td&gt;
 &lt;td&gt;中等，某些已被标记&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;数据中心代理（Datacenter）&lt;/td&gt;
 &lt;td&gt;测试/开发&lt;/td&gt;
 &lt;td&gt;高风险，Stripe Radar 会标记&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;移动代理（Mobile）&lt;/td&gt;
 &lt;td&gt;高价值交易&lt;/td&gt;
 &lt;td&gt;最安全但最贵&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;生产环境建议住宅代理。数据中心 IP 在 Stripe 的风控系统中通常有较高的 risk score。&lt;/p&gt;
&lt;h3 id="85-代理故障降级"&gt;8.5 代理故障降级
&lt;/h3&gt;&lt;p&gt;代理不可用时的处理策略：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_proxy_with_fallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 降级：尝试其他国家的代理&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;acquire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;US&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 最终降级：直连（仅限测试环境）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow_direct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;NoProxyAvailableError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：不要在生产环境用直连降级——数据中心 IP 直连 Stripe 几乎必被拒。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="九状态机设计"&gt;九、状态机设计
&lt;/h2&gt;&lt;p&gt;完整的协议支付需要处理多种失败场景和重试逻辑。最佳实践是使用状态机：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;初始化 → 刷新Session → 检查权限 → 获取定价 → 创建Checkout
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ ↓ ↓ ↓ ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 失败→退出 失败→退出 已有订阅→跳过 失败→退出 失败→退出
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 更新税务 → 创建Token → 确认支付
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ ↓ ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 失败→退出 卡被拒→换卡 3DS→验证
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 成功 / 失败
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个状态转移都需要处理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;卡被拒（Card Declined）&lt;/strong&gt;：分类拒绝原因（余额不足/CVV错误/风控拒绝），决定是换卡重试还是放弃&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3DS 验证&lt;/strong&gt;：进入 Challenge 流程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session 过期&lt;/strong&gt;：创建新 Session 重试&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rate Limiting&lt;/strong&gt;：TLS 指纹轮换 + 退避重试&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="91-decline-错误分类"&gt;9.1 Decline 错误分类
&lt;/h3&gt;&lt;p&gt;Stripe 的卡拒绝错误不是一个笼统的&amp;quot;失败&amp;quot;，而是分为可重试和不可重试两类：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;可重试（换卡后重试）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;generic_decline&lt;/code&gt; — 最常见，通常是风控拒绝，换卡几乎总能过&lt;/li&gt;
&lt;li&gt;&lt;code&gt;insufficient_funds&lt;/code&gt; — 余额不足，充值后或换卡可过&lt;/li&gt;
&lt;li&gt;&lt;code&gt;processing_error&lt;/code&gt; — Stripe 侧临时错误，直接重试&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;不可重试（放弃本次 session）&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;stolen_card&lt;/code&gt; / &lt;code&gt;lost_card&lt;/code&gt; — 卡已被标记，不能再用&lt;/li&gt;
&lt;li&gt;&lt;code&gt;card_not_supported&lt;/code&gt; — 卡类型不支持（如某些预付卡）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;currency_not_supported&lt;/code&gt; — 卡不支持目标币种&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;需要特殊处理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;incorrect_cvc&lt;/code&gt; — CVC 错误，检查是否传错了&lt;/li&gt;
&lt;li&gt;&lt;code&gt;expired_card&lt;/code&gt; — 卡过期，需要从卡池中移除&lt;/li&gt;
&lt;li&gt;&lt;code&gt;card_velocity_exceeded&lt;/code&gt; — 卡的交易频率超限，冷却后可重试&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="92-fast-path-优化"&gt;9.2 Fast-Path 优化
&lt;/h3&gt;&lt;p&gt;一个重要的状态机优化：&lt;strong&gt;如果 PI 已经 &lt;code&gt;succeeded&lt;/code&gt;，跳过 hCaptcha&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;HAR 中发现，有时 confirm 返回 &lt;code&gt;requires_action&lt;/code&gt;（hCaptcha challenge），但 PI 实际上已经扣款成功。这时如果盲目去解 captcha，反而可能出错。&lt;/p&gt;
&lt;p&gt;正确做法是在收到 &lt;code&gt;requires_action&lt;/code&gt; 后，&lt;strong&gt;先查询 PI 状态&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/v1/payment_intents/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pi_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pk_live&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;succeeded&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 已经扣款成功，跳过 captcha&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;PaymentResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SUCCESS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;requires_action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 确实需要解 captcha&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;solve_hcaptcha&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="93-hosted-模式轮询"&gt;9.3 Hosted 模式轮询
&lt;/h3&gt;&lt;p&gt;cs_live_ 模式在 confirm 之后需要轮询 &lt;code&gt;payment_page&lt;/code&gt; 状态：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;POST https://api.stripe.com/v1/payment_pages/{cs_live_xxx}/poll
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ {&amp;#34;payment_intent&amp;#34;: &amp;#34;pi_xxx&amp;#34;, &amp;#34;status&amp;#34;: &amp;#34;open&amp;#34;} // 等待中
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ {&amp;#34;payment_intent&amp;#34;: &amp;#34;pi_xxx&amp;#34;, &amp;#34;payment_object_status&amp;#34;: &amp;#34;requires_action&amp;#34;} // 需要 captcha
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ {&amp;#34;payment_intent&amp;#34;: &amp;#34;pi_xxx&amp;#34;, &amp;#34;payment_object_status&amp;#34;: &amp;#34;succeeded&amp;#34;} // 完成
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;轮询间隔建议 2-3 秒，最多轮询 60 秒。如果超时未变化，检查是否需要手动 approve。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="十安全边界与最佳实践"&gt;十、安全边界与最佳实践
&lt;/h2&gt;&lt;h3 id="101-session-隔离"&gt;10.1 Session 隔离
&lt;/h3&gt;&lt;p&gt;每次支付使用独立的 Session，不复用已失败的 Session。特别是 Hosted 模式下，&lt;code&gt;approve&lt;/code&gt; 操作后的 Session 不可再次使用。&lt;/p&gt;
&lt;h3 id="102-dry-run-模式"&gt;10.2 Dry-Run 模式
&lt;/h3&gt;&lt;p&gt;所有实现都应支持 Dry-Run 模式——执行完整流程但不提交最终的 &lt;code&gt;confirm&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commit_payments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confirm_payment_intent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pi_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Dry-run: skipping confirm&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这对于调试非常重要：你可以验证整个流程（创建 session、生成 token、税务计算）都正确，而不实际扣钱。&lt;/p&gt;
&lt;h3 id="103-不要存储敏感数据"&gt;10.3 不要存储敏感数据
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;信用卡号仅在内存中使用，支付完成后立即清除&lt;/li&gt;
&lt;li&gt;Session token 不应写入日志（log 中用 &lt;code&gt;***&lt;/code&gt; 替代）&lt;/li&gt;
&lt;li&gt;代理凭据不要硬编码，用环境变量或加密存储&lt;/li&gt;
&lt;li&gt;Stripe Publishable Key 可以写入代码（它本来就是公开的），但 Secret Key 绝不能出现在客户端&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="104-请求节奏模拟"&gt;10.4 请求节奏模拟
&lt;/h3&gt;&lt;p&gt;纯协议支付的一个风险是请求速度&lt;strong&gt;太快&lt;/strong&gt;。浏览器用户从打开支付页面到点击确认，通常需要 15-60 秒。如果你的自动化在 2 秒内完成全部请求，Stripe Radar 可能标记为异常。&lt;/p&gt;
&lt;p&gt;建议在关键步骤间加入随机延迟：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# 创建 session 后&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# 更新税务后&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# 创建 token 后&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;# confirm 前&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;总时间控制在 8-30 秒之间比较自然。&lt;/p&gt;
&lt;h3 id="105-并发控制"&gt;10.5 并发控制
&lt;/h3&gt;&lt;p&gt;批量处理多个账号时，不要让所有请求同时发出：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stripe API&lt;/strong&gt;：有 rate limit，同一 publishable key 下大量并发 confirm 会触发 429&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;平台 API&lt;/strong&gt;：多个账号同时操作可能触发风控&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;代理池&lt;/strong&gt;：每个代理 IP 上同时只运行 1-2 个支付流程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建议使用信号量（semaphore）限制并发：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Semaphore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 最多同时处理 3 个支付&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;sem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="十一实现对比"&gt;十一、实现对比
&lt;/h2&gt;&lt;p&gt;文末提供的 5 套实现覆盖了不同的技术栈和使用场景：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;实现&lt;/th&gt;
 &lt;th&gt;语言/框架&lt;/th&gt;
 &lt;th&gt;模式&lt;/th&gt;
 &lt;th&gt;特点&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;saas-pay-sdk&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Python / CLI&lt;/td&gt;
 &lt;td&gt;Custom&lt;/td&gt;
 &lt;td&gt;最精简的 CLI 工具，TLS 指纹轮换，daemon 上报&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;subscription-manager&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Python Flask / Web UI&lt;/td&gt;
 &lt;td&gt;Both&lt;/td&gt;
 &lt;td&gt;完整 Web 管理面板，支持绑卡、查账单、升级降级&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;payment-gateway&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Python Flask / Web UI&lt;/td&gt;
 &lt;td&gt;Both&lt;/td&gt;
 &lt;td&gt;批量支付面板，账号池管理，barrier 模式批量确认&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;checkout-server&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Python / Local HTTP&lt;/td&gt;
 &lt;td&gt;Both&lt;/td&gt;
 &lt;td&gt;本地支付编排服务器，Playwright 降级，SQLite 持久化&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;protocol-engine&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Node.js / Modules&lt;/td&gt;
 &lt;td&gt;Both&lt;/td&gt;
 &lt;td&gt;模块化协议引擎，CycleTLS，动态 Stripe.js 版本抓取&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;每个实现都有自己的优缺点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想要最快上手 → &lt;code&gt;saas-pay-sdk&lt;/code&gt;（纯 CLI，pip install 即可）&lt;/li&gt;
&lt;li&gt;想要可视化管理 → &lt;code&gt;subscription-manager&lt;/code&gt;（Web UI）&lt;/li&gt;
&lt;li&gt;想要批量处理 → &lt;code&gt;payment-gateway&lt;/code&gt;（账号池 + 批量模式）&lt;/li&gt;
&lt;li&gt;想要理解底层 → &lt;code&gt;protocol-engine&lt;/code&gt;（模块化，可以单独引用任何一层）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="下载"&gt;下载
&lt;/h2&gt;&lt;p&gt;以下是 5 套完整实现的源码下载链接：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;项目&lt;/th&gt;
 &lt;th&gt;下载链接&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;saas-pay-sdk&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/downloads/saas-pay-sdk.zip" &gt;下载&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Python CLI 工具&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;subscription-manager&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/downloads/subscription-manager.zip" &gt;下载&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Flask Web 管理面板&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;payment-gateway&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/downloads/payment-gateway.zip" &gt;下载&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;批量支付网关面板&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;checkout-server&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/downloads/checkout-server.zip" &gt;下载&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;本地支付编排服务器&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;protocol-engine&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/downloads/protocol-engine.zip" &gt;下载&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Node.js 协议引擎模块&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：所有源码中的 API Key、域名、账号信息均已替换为占位符。使用前需要根据你的实际环境配置 &lt;code&gt;.env&lt;/code&gt; 文件。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="推荐虚拟卡平台"&gt;推荐：虚拟卡平台
&lt;/h2&gt;&lt;p&gt;协议支付离不开信用卡。如果你需要一张支持 Stripe 的虚拟信用卡用于订阅海外 SaaS 服务（ChatGPT、Claude、Cursor 等），推荐 &lt;strong&gt;ZovoCard&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持 Visa / Mastercard，全球 Stripe 商户可用&lt;/li&gt;
&lt;li&gt;即开即用，无需实体卡，支持 USDT 充值&lt;/li&gt;
&lt;li&gt;多卡管理，适合批量订阅场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a class="link" href="https://zovocard.com/register?invite=DW6AYPAP" target="_blank" rel="noopener"
 &gt;注册 ZovoCard →&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;如果你对支付安全感兴趣，欢迎加微信群交流。&lt;/em&gt;&lt;/p&gt;</description></item><item><title>提链 — ChatGPT 跨区支付链接提取的完整技术拆解</title><link>https://blog.7qiang.com/posts/chatgpt-paylink-extraction-technical-guide/</link><pubDate>Sat, 01 Aug 2026 20:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/chatgpt-paylink-extraction-technical-guide/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post 提链 — ChatGPT 跨区支付链接提取的完整技术拆解" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 免责声明&lt;/strong&gt;：本文仅供安全研究与技术讨论。文中不提供可直接复用的攻击工具或完整利用脚本。本文目的是帮助开发者理解 Stripe 跨区支付的工作原理，以及为什么某些支付方式在不同区域可见性不同。提链工具不篡改金额、不注入优惠码，用户以正常价格通过本地支付方式完成订阅。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;“提链&amp;quot;这个词，最近在各种 ChatGPT 合租群、代充群里频繁出现。&lt;/p&gt;
&lt;p&gt;什么意思呢？简单来说：&lt;strong&gt;把 ChatGPT 的信用卡结账页面，转换成一个本地支付方式（iDEAL / UPI / PIX / Kakao Pay / GCash…）的跳转链接。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你可能会问——OpenAI 不是只接受信用卡吗？&lt;/p&gt;
&lt;p&gt;准确地说，&lt;strong&gt;OpenAI 的前端只展示信用卡表单&lt;/strong&gt;。但后端用的是 Stripe，而 Stripe 在全球支持几十种本地支付方式。这些支付方式对应的国家、货币、支付类型都不同，OpenAI 在结账页面上做了筛选，只让你看到&amp;quot;信用卡 / 借记卡&amp;quot;一个选项。&lt;/p&gt;
&lt;p&gt;但 Stripe 的能力是在的。只要你能在创建 Checkout Session 时告诉 Stripe “我要用荷兰 iDEAL 支付”——Stripe 就会老老实实返回一个 iDEAL 的跳转链接。&lt;/p&gt;
&lt;p&gt;提链，就是&lt;strong&gt;从 Stripe 的 Checkout Session 里把这个跳转链接提出来&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;上一篇文章我们拆了 &lt;a class="link" href="https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/" target="_blank" rel="noopener"
 &gt;0 PHP 白嫖 ChatGPT Plus&lt;/a&gt; 的跨区定价混淆攻击。那个是利用区域信号碎片化 + 工具注入参数让金额变成 0——纯粹的漏洞利用，现在大概率已经被修了。&lt;/p&gt;
&lt;p&gt;而提链不同。&lt;strong&gt;提链是正常的商业行为&lt;/strong&gt;——用户以当地正常价格、通过当地正常的支付方式付款。只不过 OpenAI 的 UI 不展示这些选项，你得自己绕过前端的限制，直接和 Stripe 对话。&lt;/p&gt;
&lt;p&gt;今天把它从协议层完整拆开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一为什么需要提链信用卡的困境"&gt;一、为什么需要提链——信用卡的困境
&lt;/h2&gt;&lt;p&gt;全球 80 亿人，有信用卡的不到 20 亿。&lt;/p&gt;
&lt;p&gt;印度人用 UPI，巴西人用 PIX，荷兰人用 iDEAL，韩国人用 Kakao Pay，越南人用 MoMo。这些不是什么小众支付方式——它们在各自国家的渗透率往往超过 70%。&lt;/p&gt;
&lt;p&gt;但 OpenAI 的结账页面只给你一个 Stripe Elements 信用卡表单：卡号、有效期、CVC。没有信用卡？不好意思。&lt;/p&gt;
&lt;p&gt;讽刺的是，Stripe 本身是支持这些本地支付方式的。OpenAI 创建 Checkout Session 时，就是通过 &lt;code&gt;billing_details.country&lt;/code&gt; 和 &lt;code&gt;billing_details.currency&lt;/code&gt; 来决定哪些支付方式可用。&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;国家&lt;/th&gt;
 &lt;th&gt;货币&lt;/th&gt;
 &lt;th&gt;可用支付方式&lt;/th&gt;
 &lt;th&gt;月费&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;??🇱 荷兰&lt;/td&gt;
 &lt;td&gt;EUR&lt;/td&gt;
 &lt;td&gt;iDEAL&lt;/td&gt;
 &lt;td&gt;€20&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇮🇳 印度&lt;/td&gt;
 &lt;td&gt;INR&lt;/td&gt;
 &lt;td&gt;UPI&lt;/td&gt;
 &lt;td&gt;₹1,950&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇧🇷 巴西&lt;/td&gt;
 &lt;td&gt;BRL&lt;/td&gt;
 &lt;td&gt;PIX&lt;/td&gt;
 &lt;td&gt;R$99.90&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;??🇭 瑞士&lt;/td&gt;
 &lt;td&gt;CHF&lt;/td&gt;
 &lt;td&gt;TWINT&lt;/td&gt;
 &lt;td&gt;CHF 20&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇰🇷 韩国&lt;/td&gt;
 &lt;td&gt;KRW&lt;/td&gt;
 &lt;td&gt;Kakao Pay&lt;/td&gt;
 &lt;td&gt;₩26,400&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇵🇱 波兰&lt;/td&gt;
 &lt;td&gt;PLN&lt;/td&gt;
 &lt;td&gt;BLIK&lt;/td&gt;
 &lt;td&gt;zł79.99&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;🇻🇳 越南&lt;/td&gt;
 &lt;td&gt;VND&lt;/td&gt;
 &lt;td&gt;MoMo&lt;/td&gt;
 &lt;td&gt;₫460,000&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;??🇭 菲律宾&lt;/td&gt;
 &lt;td&gt;PHP&lt;/td&gt;
 &lt;td&gt;GCash&lt;/td&gt;
 &lt;td&gt;₱990&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这些价格不是我编的——它们真实存在于 Stripe 的 Checkout Session 响应里。OpenAI 按照区域设置了不同的定价，Stripe 负责处理。只是 OpenAI 的前端选择性地隐藏了这些支付方式。&lt;/p&gt;
&lt;p&gt;提链工具要做的，就是&lt;strong&gt;绕过前端的限制，以目标国家/货币创建 Checkout Session，然后从 Stripe 的 confirm 响应中提取第三方支付的跳转链接&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二提链的技术原理四步拆解"&gt;二、提链的技术原理——四步拆解
&lt;/h2&gt;&lt;h3 id="step-1获取-access-token"&gt;Step 1：获取 Access Token
&lt;/h3&gt;&lt;p&gt;登录 chatgpt.com，在浏览器中打开：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://chatgpt.com/api/auth/session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;返回一个 JSON：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;user&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;user-xxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;...&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;accessToken&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;accessToken&lt;/code&gt; 是一个标准 JWT，有效期通常 5~10 分钟。它是后续所有 API 调用的认证凭据。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;提链工具同时也支持直接粘贴 &lt;code&gt;__Secure-next-auth.session-token&lt;/code&gt; cookie——这个 cookie 可以用来换取新的 access token。但最简单的方式还是直接复制上面的 JSON。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="step-2创建-checkout-session"&gt;Step 2：创建 Checkout Session
&lt;/h3&gt;&lt;p&gt;用 Access Token 调用 OpenAI 的 checkout API：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://chatgpt.com/backend-api/payments/checkout
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Authorization: Bearer eyJ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;entry_point&amp;#34;: &amp;#34;all_plans_pricing_modal&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;plan_name&amp;#34;: &amp;#34;chatgptplusplan&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;billing_details&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;country&amp;#34;: &amp;#34;NL&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;currency&amp;#34;: &amp;#34;EUR&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt; &amp;#34;checkout_ui_mode&amp;#34;: &amp;#34;custom&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;关键参数是 &lt;code&gt;billing_details&lt;/code&gt; 里的 &lt;code&gt;country&lt;/code&gt; 和 &lt;code&gt;currency&lt;/code&gt;。这两个字段决定了 Stripe 在初始化支付页面时会提供哪些支付方式。传 &lt;code&gt;NL&lt;/code&gt; + &lt;code&gt;EUR&lt;/code&gt;，Stripe 就会在 &lt;code&gt;payment_method_types&lt;/code&gt; 里包含 &lt;code&gt;ideal&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;响应：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;checkout_session_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;oaics_xxxxxxxxxxxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;processor_entity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;openai_llc&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;publishable_key&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pk_live_xxxxxxxxxxxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_method_types&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ideal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;oaics_&lt;/code&gt; 前缀是 OpenAI 定制的 Stripe Checkout Session ID 格式。&lt;/p&gt;
&lt;h3 id="step-3stripe-协议流程"&gt;Step 3：Stripe 协议流程
&lt;/h3&gt;&lt;p&gt;拿到 Checkout Session ID 后，需要完成一系列 Stripe API 调用。这是提链的核心技术含量所在——不是简单地 POST 一个接口就完事了。&lt;/p&gt;
&lt;p&gt;完整的协议流程有 12 步：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;┌──────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 1. Session Refresh │ (用 cookie 换新的 AT)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 2. Create Checkout │ (拿 AT 创建 checkout session)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 3. Stripe Init │ (初始化支付页面状态)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 4. Update OAI Taxes │ (通知 OpenAI 更新税务)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 5. Stripe Tax Region │ (更新 Stripe 税务区域)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 6. Stripe Customer Data │ (更新客户信息)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 7. OAI Checkout Snapshot │ (保存 checkout 快照)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 8. Pre-Confirm │ (部分支付方式需要)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 9. Create Payment Method │ (创建 PM，类型=ideal/upi/pix...)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 10. Confirm │ (确认支付，触发重定向)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 11. Approve │ (如果需要额外批准)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ 12. Extract Redirect URL │ (从 confirm 响应中提取跳转链接)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└──────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每一步都有自己的参数格式和必要的状态管理。Stripe 的 Checkout Session 是有状态的——它跟踪你的 &lt;code&gt;eid&lt;/code&gt;（element ID）、&lt;code&gt;mrid&lt;/code&gt;（merchant reference ID）、&lt;code&gt;expected_amount&lt;/code&gt; 等等。如果你跳过了某一步或者参数不对，后续步骤就会报错。&lt;/p&gt;
&lt;h3 id="step-4提取支付链接"&gt;Step 4：提取支付链接
&lt;/h3&gt;&lt;p&gt;第 10 步 &lt;code&gt;Confirm&lt;/code&gt; 之后，如果支付方式是重定向类型（iDEAL、UPI、PIX 等都是），Stripe 会在响应中返回一个 &lt;code&gt;redirect_url&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;requires_action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;next_action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;redirect_to_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;redirect_to_url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;url&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://hooks.stripe.com/redirect/authenticate/src_xxx?client_secret=...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 URL 就是最终的支付链接。用户在浏览器中打开它，会被重定向到对应的支付方式页面（iDEAL 的银行选择页、UPI 的付款确认页、PIX 的二维码页面等）。完成支付后，Stripe webhook 通知 OpenAI，OpenAI 确认订阅生效。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;整个流程，金额由 Stripe 的价格表决定，提链工具不篡改任何金额字段。&lt;/strong&gt; 用户付的就是对应国家/地区的标准月费。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="三stripe-checkout-session-的内部结构"&gt;三、Stripe Checkout Session 的内部结构
&lt;/h2&gt;&lt;p&gt;拆开看，Stripe Checkout Session 里藏了不少有趣的东西。&lt;/p&gt;
&lt;p&gt;第 3 步 &lt;code&gt;Stripe Init&lt;/code&gt; 调用的是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/payment_pages/{oaics_xxx}/init
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;响应中最关键的几个字段：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_method_types&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ideal&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;line_items&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ChatGPT Plus subscription&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eur&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;shipping_address_collection&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_method_configuration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;merchant_reference_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;mri_xxx&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;customer_session_client_secret&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;cuss_xxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;session_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;oaics_xxx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;payment_method_types&lt;/code&gt; 是关键——它决定了这个 Checkout Session 支持哪些支付方式。同一个接口，传不同的 &lt;code&gt;country&lt;/code&gt; / &lt;code&gt;currency&lt;/code&gt;，返回的 &lt;code&gt;payment_method_types&lt;/code&gt; 就不同。&lt;/p&gt;
&lt;p&gt;这也解释了为什么 OpenAI 前端只显示信用卡——它在创建 Checkout Session 时没有传目标国家的 &lt;code&gt;billing_details&lt;/code&gt;，或者传的是用户账号的注册国家（通常是 US），所以 Stripe 只返回 &lt;code&gt;[&amp;quot;card&amp;quot;]&lt;/code&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四八种支付方式的技术对比"&gt;四、八种支付方式的技术对比
&lt;/h2&gt;&lt;p&gt;每种支付方式在 Stripe 层面的行为略有不同：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;支付方式&lt;/th&gt;
 &lt;th&gt;PM Type&lt;/th&gt;
 &lt;th&gt;国家&lt;/th&gt;
 &lt;th&gt;货币&lt;/th&gt;
 &lt;th&gt;需要 pre_confirm&lt;/th&gt;
 &lt;th&gt;重定向类型&lt;/th&gt;
 &lt;th&gt;特殊处理&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;iDEAL&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;ideal&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;NL&lt;/td&gt;
 &lt;td&gt;EUR&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;银行选择页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;UPI&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;upi&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;IN&lt;/td&gt;
 &lt;td&gt;INR&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;VPA 输入页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;PIX&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;pix&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;BR&lt;/td&gt;
 &lt;td&gt;BRL&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;二维码页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;TWINT&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;twint&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;CH&lt;/td&gt;
 &lt;td&gt;CHF&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;App 跳转&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Kakao Pay&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;kakao_pay&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;KR&lt;/td&gt;
 &lt;td&gt;KRW&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;Kakao 确认页&lt;/td&gt;
 &lt;td&gt;需要 pre_confirm 调用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;BLIK&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;blik&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PL&lt;/td&gt;
 &lt;td&gt;PLN&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;6位码输入&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;MoMo&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;momo&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;VN&lt;/td&gt;
 &lt;td&gt;VND&lt;/td&gt;
 &lt;td&gt;✓&lt;/td&gt;
 &lt;td&gt;MoMo App&lt;/td&gt;
 &lt;td&gt;需要 pre_confirm 调用&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GCash&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;gcash&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PH&lt;/td&gt;
 &lt;td&gt;PHP&lt;/td&gt;
 &lt;td&gt;✗&lt;/td&gt;
 &lt;td&gt;GCash 确认页&lt;/td&gt;
 &lt;td&gt;无&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;大多数支付方式流程一致：创建 PM → Confirm → 提取 redirect URL。但 Kakao Pay 和 MoMo 需要额外的 &lt;code&gt;pre_confirm&lt;/code&gt; 调用——如果跳过这一步，Confirm 会返回 &lt;code&gt;incomplete&lt;/code&gt; 状态，没有 redirect URL。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pre_confirm&lt;/code&gt; 的请求格式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-http" data-lang="http"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;POST https://api.stripe.com/v1/payment_pages/{oaics_xxx}/pre_confirm
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;Content-Type: application/x-www-form-urlencoded
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;expected_payment_method_type=kakao_pay
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;看起来简单，但如果你不知道有这一步，Kakao Pay 的提链就会卡在最后一步——所有参数都对了，就是拿不到链接。这类细节只有通过 HAR 抓包 Stripe 的完整结账流程才能发现。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="五从-0-php-到提链漏洞与工具的边界"&gt;五、从 0 PHP 到提链——漏洞与工具的边界
&lt;/h2&gt;&lt;p&gt;上一篇文章分析了 &lt;a class="link" href="https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/" target="_blank" rel="noopener"
 &gt;0 PHP 攻击&lt;/a&gt;，里面提到第三方工具在创建 Checkout Session 时可能注入了零元参数（如 &lt;code&gt;promo_campaign&lt;/code&gt;）。那么问题来了：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;提链工具和那个&amp;quot;CDK 提炼&amp;quot;工具是一回事吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不是。区别在于：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;0 PHP “提炼&amp;quot;工具&lt;/th&gt;
 &lt;th&gt;正常提链工具&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;目的&lt;/td&gt;
 &lt;td&gt;白嫖（amount = 0）&lt;/td&gt;
 &lt;td&gt;正常付款&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;是否注入优惠码&lt;/td&gt;
 &lt;td&gt;是（&lt;code&gt;promo_campaign&lt;/code&gt;、&lt;code&gt;discount&lt;/code&gt; 等）&lt;/td&gt;
 &lt;td&gt;否&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;金额来源&lt;/td&gt;
 &lt;td&gt;工具操纵&lt;/td&gt;
 &lt;td&gt;Stripe 价格表&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;合法性&lt;/td&gt;
 &lt;td&gt;灰色/违法&lt;/td&gt;
 &lt;td&gt;正常商业行为&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;正常的提链工具&lt;strong&gt;不注入任何优惠参数&lt;/strong&gt;。它只是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用你的 AT 创建一个以目标国家/货币计价的 Checkout Session&lt;/li&gt;
&lt;li&gt;走完 Stripe 的协议流程&lt;/li&gt;
&lt;li&gt;把 redirect URL 返回给你&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你付的是 Stripe 价格表上对应区域的标准价格——荷兰 €20/月，印度 ₹1,950/月，巴西 R$99.90/月。没有优惠码，没有金额篡改。&lt;/p&gt;
&lt;p&gt;正常的提链工具的设计原则应该是：&lt;strong&gt;不注入 &lt;code&gt;promo_campaign&lt;/code&gt;，不传 &lt;code&gt;discount&lt;/code&gt;，不覆盖 &lt;code&gt;amount&lt;/code&gt;。&lt;/strong&gt; 工具只做一件事——把 Stripe 的本地支付能力释放出来，让没有信用卡的用户也能正常订阅。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="六协议细节那些让你卡住的坑"&gt;六、协议细节——那些让你卡住的坑
&lt;/h2&gt;&lt;p&gt;如果你自己尝试过写提链脚本，你一定踩过这些坑。&lt;/p&gt;
&lt;h3 id="坑-1ip-一致性"&gt;坑 1：IP 一致性
&lt;/h3&gt;&lt;p&gt;Stripe Checkout Session 在创建和确认时需要使用同一个 IP。如果你用代理 A 创建了 Checkout Session，又用代理 B 去 Confirm——Stripe 会返回 &lt;code&gt;invalid_request_error&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这不是 Stripe 的 bug，是安全机制。Stripe 检测到 session 的创建 IP 和确认 IP 不一致，会认为可能是中间人攻击。&lt;/p&gt;
&lt;p&gt;解决方案：&lt;strong&gt;整个 12 步流程使用同一个 HTTP client（或 cookie jar），确保所有请求走同一个出口 IP。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="坑-2expected_amount"&gt;坑 2：&lt;code&gt;expected_amount&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;Stripe 的 Confirm 请求需要传 &lt;code&gt;expected_amount&lt;/code&gt; 参数，值必须和 Checkout Session 的金额一致。这个值从哪来？&lt;/p&gt;
&lt;p&gt;最准确的来源是第 4 步 &lt;code&gt;Update Taxes&lt;/code&gt; 的响应。这一步把账单地址传给 OpenAI，OpenAI 计算含税金额后写入 Checkout Session，然后返回一个 snapshot，里面有 &lt;code&gt;amount_total&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;snapshot&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;amount_total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;eur&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果你用 Init 步骤的金额去 Confirm，可能会因为税后金额不一致而失败。&lt;/p&gt;
&lt;h3 id="坑-3stripe-session-state"&gt;坑 3：Stripe Session State
&lt;/h3&gt;&lt;p&gt;Stripe 的 Checkout Session 维护了一个内部状态。每次 API 调用后，响应里都会更新 &lt;code&gt;eid&lt;/code&gt;、&lt;code&gt;mrid&lt;/code&gt; 等字段。下一次调用必须带上最新的状态值。&lt;/p&gt;
&lt;p&gt;这意味着你不能简单地并行调用 12 步——必须严格按顺序执行，每一步用上一步返回的最新状态。&lt;/p&gt;
&lt;h3 id="坑-4accept-language-和-guid"&gt;坑 4：&lt;code&gt;Accept-Language&lt;/code&gt; 和 &lt;code&gt;guid&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;这两个是容易忽略的细节：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stripe API 会根据 &lt;code&gt;Accept-Language&lt;/code&gt; 请求头返回不同的 &lt;code&gt;locale&lt;/code&gt; 配置。如果你不传，默认是 &lt;code&gt;en-US&lt;/code&gt;，但某些支付方式在特定 locale 下行为不同。&lt;/li&gt;
&lt;li&gt;Stripe 的某些内部 API 需要一个 &lt;code&gt;guid&lt;/code&gt; 参数，格式是 &lt;code&gt;xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx&lt;/code&gt;（UUID v4）。不是随便写的——格式不对会被拒绝。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="坑-5超时"&gt;坑 5：超时
&lt;/h3&gt;&lt;p&gt;整个流程从创建 Checkout Session 到拿到 redirect URL，通常需要 10~30 秒。某些支付方式（如 Kakao Pay）可能需要更长时间，因为 Stripe 需要和第三方支付系统交互。&lt;/p&gt;
&lt;p&gt;如果 Confirm 之后没有立刻返回 redirect URL，需要轮询 Init 接口。URL 可能在 Confirm 后几秒到几十秒内才出现。我们的工具最多等 90 秒——超过这个时间通常意味着哪里出了问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七开源提链脚本"&gt;七、开源提链脚本
&lt;/h2&gt;&lt;p&gt;说了这么多原理，给一些现成的实现参考。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 时效性声明&lt;/strong&gt;：以下脚本收集于 2026 年 7 月。OpenAI/Stripe 的 API 接口、Stripe runtime 版本、风控策略随时可能变更，脚本&lt;strong&gt;可能已不可用&lt;/strong&gt;。仅供学习协议流程和理解技术原理，不保证当前可运行。使用前请自行验证。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h3 id="多支付方式提链套件"&gt;多支付方式提链套件
&lt;/h3&gt;&lt;p&gt;来源：社区开源的 &lt;code&gt;ideal-link-extractor&lt;/code&gt;，MIT License。&lt;/p&gt;
&lt;p&gt;包含 6 种支付方式的独立提取脚本：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;脚本&lt;/th&gt;
 &lt;th&gt;支付方式&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;ideal_qr_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;iDEAL（荷兰）&lt;/td&gt;
 &lt;td&gt;核心脚本（3263 行），其他脚本的参考实现&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;upi/upi_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;UPI（印度）&lt;/td&gt;
 &lt;td&gt;含多代理轮转、并行模式&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;pix/pix_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PIX（巴西）&lt;/td&gt;
 &lt;td&gt;二维码提取&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;twint/twint_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;TWINT（瑞士）&lt;/td&gt;
 &lt;td&gt;App 跳转链接&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;kakao/kakao_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Kakao Pay（韩国）&lt;/td&gt;
 &lt;td&gt;含 pre_confirm 处理&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;blik/blik_qr_extract.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;BLIK（波兰）&lt;/td&gt;
 &lt;td&gt;6 位码输入链接&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;detect_payment_methods.py&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;通用&lt;/td&gt;
 &lt;td&gt;按国家检测可用的 Stripe 支付方式&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;依赖：&lt;code&gt;requests&lt;/code&gt;、&lt;code&gt;curl_cffi&lt;/code&gt;、&lt;code&gt;qrcode[pil]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;基本用法（以 UPI 为例）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pip install requests curl_cffi
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;UPI_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;eyJhbGciOi...&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;UPI_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;socks5://user:pass@host:port&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# 自备代理&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python upi_extract.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每个脚本通过环境变量配置代理（&lt;code&gt;XXX_PROXY&lt;/code&gt;）和 Token（&lt;code&gt;XXX_TOKEN&lt;/code&gt; 或 &lt;code&gt;token.txt&lt;/code&gt; 文件）。代理需自备——脚本不内置任何代理。&lt;/p&gt;
&lt;h3 id="momo-提取脚本越南"&gt;MoMo 提取脚本（越南）
&lt;/h3&gt;&lt;p&gt;独立维护的 MoMo/Kakao 脚本集，包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kakao_extract.py&lt;/code&gt;——共享的会话管理和 Stripe 协议辅助函数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;momo_channel_probe.py&lt;/code&gt;——只读模式探测 MoMo 通道可用性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;momo_probe.py&lt;/code&gt;——默认只读，设置 &lt;code&gt;MOMO_EXTRACT_REDIRECT=1&lt;/code&gt; 才尝试提取&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;依赖额外的 Playwright（需要 Chromium）。&lt;/p&gt;
&lt;h3 id="脚本的核心设计"&gt;脚本的核心设计
&lt;/h3&gt;&lt;p&gt;这些脚本的技术亮点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;代理状态持久化&lt;/strong&gt;——&lt;code&gt;proxy_state.json&lt;/code&gt; 记录每个代理的健康状态、成功/失败计数、冷却时间&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日志脱敏&lt;/strong&gt;——自动将代理地址、Token 等敏感信息替换为安全标签&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;多 Worker 并行&lt;/strong&gt;——支持多线程并发提取，自动分配代理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stripe 协议完整实现&lt;/strong&gt;——Session state 管理、&lt;code&gt;eid&lt;/code&gt;/&lt;code&gt;mrid&lt;/code&gt; 追踪、tax region 更新，完整复刻浏览器行为&lt;/li&gt;
&lt;/ol&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：部分脚本包含 &lt;code&gt;promo_campaign&lt;/code&gt; 注入逻辑和 &lt;code&gt;REQUIRE_ZERO&lt;/code&gt; 选项（用于上一篇文章分析的 0 PHP 攻击路径）。这些属于漏洞利用代码，&lt;strong&gt;不建议使用&lt;/strong&gt;——OpenAI 大概率已做修复，强行使用可能导致账号风控。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="八为什么-openai-不直接提供这些支付方式"&gt;八、为什么 OpenAI 不直接提供这些支付方式？
&lt;/h2&gt;&lt;p&gt;好问题。Stripe 支持它们，OpenAI 用的是 Stripe，为什么不在结账页面上显示？&lt;/p&gt;
&lt;p&gt;几个可能的原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;合规成本&lt;/strong&gt;：每个国家的本地支付方式有不同的合规要求（反洗钱、实名验证等）。支持 iDEAL 意味着要满足荷兰的金融监管要求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;退款风险&lt;/strong&gt;：某些本地支付方式的退款/dispute 流程和信用卡不同。PIX 的退款机制、UPI 的 chargeback 流程，都需要额外的运营投入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;税务复杂度&lt;/strong&gt;：不同国家的增值税率不同，账单格式要求不同。OpenAI 需要为每个国家维护正确的税务处理。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;优先级&lt;/strong&gt;：信用卡覆盖了 OpenAI 的核心市场（北美、欧洲）。本地支付方式对应的市场（东南亚、拉美、南亚）目前可能不是优先级。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但 Stripe 的能力已经在那里了。&lt;code&gt;payment_method_types&lt;/code&gt; 里已经包含了这些方式。OpenAI 只需要在前端把选项打开。在此之前——提链工具就是桥梁。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="九和-0-php-漏洞的关系一张图说清"&gt;九、和 0 PHP 漏洞的关系——一张图说清
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 正常提链工具 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ country=NL, currency=EUR │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ NO promo_campaign │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → amount = €20 (Stripe 价格表) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → redirect URL → iDEAL 付款 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────┬───────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 同一个 API │ 同一个 API
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────▼───────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ OpenAI /payments/checkout API │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + Stripe Checkout Session │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────┬───────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌───────────▼───────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 0 PHP 攻击工具 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ country=PH, currency=PHP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + promo_campaign injection │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + cross-region confusion │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → amount = ₱0 (被操纵) │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → PH BIN 卡完成支付 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └───────────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同一个 API，两种用法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;提链&lt;/strong&gt;：合法利用 Stripe 的本地支付能力，正常价格&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0 PHP&lt;/strong&gt;：利用跨区信号碎片化 + 参数注入，把价格打到零&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;提链不是漏洞利用。它只是 Stripe 本来就有的能力，被 OpenAI 前端隐藏了而已。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="十写在最后"&gt;十、写在最后
&lt;/h2&gt;&lt;p&gt;提链的本质是一个简单的事实：&lt;strong&gt;Stripe 是一个全球支付平台，它天然支持几十种本地支付方式。OpenAI 的 UI 做了限制，但 API 没有。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;从技术角度看，12 步协议流程的每一步都有其必要性——IP 一致性、状态管理、税务更新、pre_confirm。跳过任何一步都会导致最终拿不到 redirect URL。这也是为什么&amp;quot;提链&amp;quot;不是简单地抓个包就能搞定的——你需要理解 Stripe 的完整 Checkout 流程。&lt;/p&gt;
&lt;p&gt;从商业角度看，提链让全球用户有了一种不依赖信用卡的方式来使用 ChatGPT。在信用卡渗透率不到 10% 的印度，UPI 是 8 亿人的默认支付方式。提链本身不是什么灰色操作——它只是把 Stripe 的能力释放给了真正需要的人。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;最后的最后&lt;/strong&gt;：本文提到的所有脚本和协议细节都有时效性。OpenAI 和 Stripe 的接口在持续变化——Stripe runtime version 会更新，API 字段会调整，风控规则会收紧。今天能跑的脚本，明天可能就 403 了。理解原理比收藏脚本更重要。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="附录直卡绑定另一条路"&gt;附录：直卡绑定——另一条路
&lt;/h2&gt;&lt;p&gt;除了提链，还有一种完全不同的思路：&lt;strong&gt;直接给 ChatGPT 账号绑一张卡，然后正常走 checkout。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就是&amp;quot;直卡焚决&amp;quot;方法的核心。它不绕过前端、不走第三方支付——它解决的是&amp;quot;没有美区信用卡&amp;quot;这个前置问题。&lt;/p&gt;
&lt;h3 id="原理"&gt;原理
&lt;/h3&gt;&lt;p&gt;OpenAI 的 &lt;code&gt;/backend-api/payments/payment_method&lt;/code&gt; 接口支持创建 Stripe &lt;code&gt;SetupIntent&lt;/code&gt;——这是 Stripe 的&amp;quot;先绑卡不扣款&amp;quot;机制。绑上之后，下次走 checkout 时就有卡可用了。&lt;/p&gt;
&lt;p&gt;配套的控制台脚本（在 chatgpt.com 的 Console 里运行）做了这几件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自动发现 Stripe 公钥&lt;/strong&gt;——OpenAI 有两个 Stripe 商户分片（&lt;code&gt;KslHRdbaPg&lt;/code&gt; 和 &lt;code&gt;C6h1nxGoI3&lt;/code&gt;），脚本通过 &lt;code&gt;retrieveSetupIntent&lt;/code&gt; 自动匹配当前账号属于哪个分片&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;创建 SetupIntent&lt;/strong&gt;——调用 OpenAI 的 &lt;code&gt;payment_method&lt;/code&gt; API，拿到 &lt;code&gt;client_secret&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;渲染 Stripe Card Element&lt;/strong&gt;——在页面上弹出一个模态框，内嵌 Stripe 的标准卡输入组件&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;确认绑定&lt;/strong&gt;——&lt;code&gt;stripe.confirmCardSetup&lt;/code&gt; + &lt;code&gt;set_as_default_payment_method: true&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 关键调用
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;confirmCardSetup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;payment_method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;card&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cardElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;billing_details&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;allow_redisplay&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;always&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;set_as_default_payment_method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;绑卡成功后，脚本还会调用 &lt;code&gt;/backend-api/payments/payment_methods&lt;/code&gt; 拉取当前账号的所有支付方式列表，用 &lt;code&gt;console.table&lt;/code&gt; 打印出来。你可以看到新绑的卡已经成为默认支付方式。&lt;/p&gt;
&lt;h3 id="和提链的关系"&gt;和提链的关系
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;直卡绑定&lt;/strong&gt;：解决&amp;quot;没有卡&amp;quot;的问题。需要有一张能过 Stripe AVS/3DS 验证的实体或虚拟卡。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提链&lt;/strong&gt;：解决&amp;quot;不想/不能用信用卡&amp;quot;的问题。走本地支付方式，完全不涉及信用卡。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两种方法互补——有卡的用直卡，没卡的用提链。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="相关阅读"&gt;相关阅读
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/" target="_blank" rel="noopener"
 &gt;0 PHP 白嫖 ChatGPT Plus — 跨区定价混淆攻击的完整技术拆解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-google-play-subscription-transfer/" target="_blank" rel="noopener"
 &gt;ChatGPT Plus Google Play 订阅转移漏洞分析&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 凭证转移漏洞——匿名购买 + restore 归属偷渡&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;Google Play CC Max 比例分配漏洞&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>0 PHP 白嫖 ChatGPT Plus — 跨区定价混淆攻击的完整技术拆解</title><link>https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/</link><pubDate>Tue, 28 Jul 2026 18:00:00 +0800</pubDate><guid>https://blog.7qiang.com/posts/chatgpt-plus-0php-cross-region-pricing-exploit-2026/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post 0 PHP 白嫖 ChatGPT Plus — 跨区定价混淆攻击的完整技术拆解" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 免责声明&lt;/strong&gt;：本文仅供安全研究与技术讨论。文中不提供可直接复用的攻击工具或完整利用脚本。利用类似手段对正式服务进行未授权操作属违法行为，后果自负。本文目的是帮助支付系统开发者理解跨区定价攻击面并加固防御。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="前言"&gt;前言
&lt;/h2&gt;&lt;p&gt;这两天群里炸了。&lt;/p&gt;
&lt;p&gt;有人甩出一张截图：Stripe 结账页面，产品写着 “ChatGPT Plus”，金额一栏赫然显示 &lt;strong&gt;₱0.00 PHP&lt;/strong&gt;。菲律宾比索，零元，提交即开通。&lt;/p&gt;
&lt;p&gt;评论区画风可想而知——“假的吧&amp;quot;“PS 的吧&amp;quot;“你怎么不说 Plus 倒找你钱”。&lt;/p&gt;
&lt;p&gt;然后陆陆续续有人贴出了完整流程，甚至还配了视频录屏。操作步骤不复杂，总共四步，不需要 Root，不需要 Frida，不需要改一行代码。唯一的技术含量在于——&lt;strong&gt;你得知道用什么顺序把哪些区域串起来。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就有意思了。之前我们拆过 &lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;prorationMode hook&lt;/a&gt;（改一个 &lt;code&gt;putInt&lt;/code&gt; 白嫖 Claude Max）、拆过 &lt;a class="link" href="https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/" target="_blank" rel="noopener"
 &gt;焚决脚本&lt;/a&gt;（劫持 &lt;code&gt;checkout_capabilities&lt;/code&gt; 走 SEPA 假账号）、拆过 &lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 凭证转移&lt;/a&gt;（匿名购买 + restore 归属偷渡）。那些多少都是&amp;quot;改代码&amp;quot;的路子——Hook 内存、注入响应、篡改参数。&lt;/p&gt;
&lt;p&gt;但这次不一样。这次&lt;strong&gt;什么都没改&lt;/strong&gt;。没有脚本，没有 Hook，没有中间人。攻击者只是以一种特定的顺序访问了几个网页，然后 Stripe 自己就吐出了 0 元账单。&lt;/p&gt;
&lt;p&gt;今天把它完整拆开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一攻击全景四步从-jp-到-ph从-20-到-0"&gt;一、攻击全景：四步，从 JP 到 PH，从 $20 到 ₱0
&lt;/h2&gt;&lt;p&gt;先把流传的步骤还原一下：&lt;/p&gt;
&lt;h3 id="step-1日本节点注册-openai-账号"&gt;Step 1：日本节点注册 OpenAI 账号
&lt;/h3&gt;&lt;p&gt;挂 JP 代理，开一个干净的浏览器 Profile（指纹浏览器或者 Chrome 新 Profile），去 OpenAI 官网正常注册，邮箱验证，登录确认。&lt;/p&gt;
&lt;p&gt;此时你的账号状态：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.locale = &amp;#34;ja-JP&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.billing_country = &amp;#34;JP&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.currency = &amp;#34;JPY&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;OpenAI 在注册时根据你的 IP 地理位置初始化账号的计费区域。&lt;strong&gt;这一步的目的不是&amp;quot;要日本&amp;quot;本身，而是要一个非美区的初始定价锚点。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="step-2切美国节点提取-access-token"&gt;Step 2：切美国节点，提取 Access Token
&lt;/h3&gt;&lt;p&gt;保持同一个浏览器 Profile，VPN 切到 US 节点，重新登录刚才注册的账号。然后新标签页打开：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://chatgpt.com/api/auth/session
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;浏览器返回一个 JSON，里面有 &lt;code&gt;accessToken&lt;/code&gt; 字段——一个标准 JWT。复制保存。&lt;/p&gt;
&lt;p&gt;此时你的 session 状态出现了第一层矛盾：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;account.billing_country = &amp;#34;JP&amp;#34; ← 注册时写入，持久化在 OpenAI 数据库
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;session.ip_country = &amp;#34;US&amp;#34; ← 当前 IP 推断，存在 session/edge 层
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="step-3第三方工具生成-checkout-链接"&gt;Step 3：第三方工具生成 Checkout 链接
&lt;/h3&gt;&lt;p&gt;打开某个第三方&amp;quot;CDK 提炼&amp;quot;工具站点（这类工具本质上就是一个 AT → Stripe Checkout Session 的代理），把 Access Token 粘进去，点生成。&lt;/p&gt;
&lt;p&gt;工具返回一个链接，格式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://chatgpt.com/checkout/openai_llc/oaics_xxxxxxxxxxxxxxxxxxxxx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;oaics_&lt;/code&gt; 是 OpenAI 的 Stripe Checkout Session ID 前缀。这个 session 是 OpenAI 后端基于你的 AT 创建的，包含了产品、价格、货币等信息。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关键问题来了：这个 Checkout Session 的 currency 和 amount 是怎么决定的？&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="step-4打开链接惊喜时刻"&gt;Step 4：打开链接，惊喜时刻
&lt;/h3&gt;&lt;p&gt;在美国节点下打开这个 oaics 链接。Stripe 结账页面加载出来，你看到：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ChatGPT Plus
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;₱0.00 PHP / month
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;菲律宾比索。零元。&lt;/p&gt;
&lt;p&gt;填入指定 BIN 的卡片信息（523686 或 4513），美国账单地址，提交。&lt;/p&gt;
&lt;p&gt;Stripe 收了一笔 0 元交易，OpenAI 后端确认 checkout session 成功，权益生效。回到 ChatGPT 首页，左上角已经是 Plus 了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二为什么是-0-php三种假说"&gt;二、为什么是 0 PHP？——三种假说
&lt;/h2&gt;&lt;p&gt;先澄清一个重要事实：&lt;strong&gt;OpenAI 在菲律宾是有明确定价的&lt;/strong&gt;，ChatGPT Plus 的 PH 区定价大约是 ₱990 PHP/月。所以这不是简单的&amp;quot;价格表空洞导致 fallback 到 0”——如果定价系统正常查 PH 价格表，应该返回 ₱990，不是 ₱0。&lt;/p&gt;
&lt;p&gt;那 0 是怎么来的？没有对 Checkout Session 创建过程的完整抓包，我们只能做推断。以下三种假说按可能性排序。&lt;/p&gt;
&lt;h3 id="假说一第三方工具在创建-checkout-session-时注入了零元参数最可能"&gt;假说一：第三方工具在创建 Checkout Session 时注入了零元参数（最可能）
&lt;/h3&gt;&lt;p&gt;OpenAI 的 &lt;code&gt;/backend-api/payments/checkout&lt;/code&gt; 接口接受多个参数来创建 Stripe Checkout Session。如果这个接口接受客户端传入的 &lt;code&gt;discount&lt;/code&gt;、&lt;code&gt;coupon&lt;/code&gt;、&lt;code&gt;promotion_code&lt;/code&gt; 或类似参数，且&lt;strong&gt;后端没做资格校验&lt;/strong&gt;——工具直接传了一个 100% 折扣的 coupon code 或者 &lt;code&gt;amount_override: 0&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这和之前 &lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 文章&lt;/a&gt;里分析过的 &lt;code&gt;eligible_promo_campaigns&lt;/code&gt; 注入如出一辙：客户端声称&amp;quot;我有优惠资格”，后端不校验就信了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 工具的真实请求可能长这样
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;promotion_code&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;某个内部免费试用码&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 或者
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;billing_country&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PH&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;php&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 或者更直接的
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;price_override&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;JP 注册 + US 登录制造的区域歧义，可能不是直接触发 0 元的原因，而是&lt;strong&gt;绕过风控检查的前置条件&lt;/strong&gt;——当账号区域和 IP 区域不一致时，后端对 checkout 参数的校验逻辑可能走入了一个宽松的分支。&lt;/p&gt;
&lt;h3 id="假说二跨区信号冲突导致定价路由进入异常分支"&gt;假说二：跨区信号冲突导致定价路由进入异常分支
&lt;/h3&gt;&lt;p&gt;当 OpenAI 创建 Stripe Checkout Session 时，面前有至少四个区域信号：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;信号来源&lt;/th&gt;
 &lt;th&gt;值&lt;/th&gt;
 &lt;th&gt;决定时机&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;账号注册地&lt;/td&gt;
 &lt;td&gt;JP&lt;/td&gt;
 &lt;td&gt;注册时固化&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;当前 session IP&lt;/td&gt;
 &lt;td&gt;US&lt;/td&gt;
 &lt;td&gt;每次请求实时推断&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Stripe 卡 BIN 发行国&lt;/td&gt;
 &lt;td&gt;PH&lt;/td&gt;
 &lt;td&gt;填卡时由 Stripe 推断&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;浏览器 Accept-Language&lt;/td&gt;
 &lt;td&gt;取决于浏览器设置&lt;/td&gt;
 &lt;td&gt;每次请求携带&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;正常场景下这四个信号一致——美国人在美国用美国卡买东西，直接查 US 价格表。&lt;/p&gt;
&lt;p&gt;但当 JP ≠ US ≠ PH 三个信号同时存在时，定价系统需要做一个选择。如果选择逻辑是 &lt;code&gt;if (account.country != ip.country) { use card.country }&lt;/code&gt; 这种优先级策略，而 PH 价格表虽然存在但在某个&lt;strong&gt;子路径&lt;/strong&gt;（比如&amp;quot;非本地注册用户+跨区IP&amp;quot;的特殊定价分支）中没有配置——就可能在这个特殊分支里 fallback 到 0。&lt;/p&gt;
&lt;p&gt;换句话说：&lt;strong&gt;PH 的主价格表有值（₱990），但某个异常处理路径的价格映射没覆盖到 PH&lt;/strong&gt;，攻击者通过区域信号碎片化精确命中了这条路径。&lt;/p&gt;
&lt;h3 id="假说三stripe-checkout-session-的-line_items-被工具篡改"&gt;假说三：Stripe Checkout Session 的 line_items 被工具篡改
&lt;/h3&gt;&lt;p&gt;Stripe &lt;code&gt;checkout.sessions.create&lt;/code&gt; 的 &lt;code&gt;line_items&lt;/code&gt; 参数包含 &lt;code&gt;price&lt;/code&gt; 或 &lt;code&gt;price_data&lt;/code&gt;。如果 OpenAI 的接口允许客户端指定 &lt;code&gt;price_data.unit_amount&lt;/code&gt;（哪怕是间接的），工具就能在创建 session 时直接把金额写成 0。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;line_items: [{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; price_data: {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; currency: &amp;#34;php&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; unit_amount: 0, // 直接指定 0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; product: &amp;#34;prod_xxx&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; recurring: { interval: &amp;#34;month&amp;#34; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; quantity: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这种情况下 PH BIN 的作用不是&amp;quot;触发 PH 价格表&amp;quot;，而是&lt;strong&gt;让 currency 字段说得通&lt;/strong&gt;——如果你传 &lt;code&gt;currency: &amp;quot;php&amp;quot;&lt;/code&gt; 但卡是 US 卡，Stripe 可能会有额外的 currency mismatch 警告。PH 卡 + PHP 货币是自洽的。&lt;/p&gt;
&lt;h3 id="为什么是-ph-bin"&gt;为什么是 PH BIN？
&lt;/h3&gt;&lt;p&gt;无论哪种假说，流传的两个 BIN 都有其作用：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;BIN&lt;/th&gt;
 &lt;th&gt;网络&lt;/th&gt;
 &lt;th&gt;发行国&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;523686&lt;/td&gt;
 &lt;td&gt;Mastercard&lt;/td&gt;
 &lt;td&gt;菲律宾&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4513xx&lt;/td&gt;
 &lt;td&gt;Visa&lt;/td&gt;
 &lt;td&gt;取决于具体发行行&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;523686 开头的卡填入 Stripe 表单时，Stripe 内部标记 &lt;code&gt;payment_method.card.country = &amp;quot;PH&amp;quot;&lt;/code&gt;。这个信号在整个支付流中传播。PH BIN 的作用至少有两个：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;让 PHP 货币的 checkout session 能正常完成&lt;/strong&gt;——卡的发行国和结账货币匹配&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可能绕过 OpenAI 的 card-country vs account-country 风控规则&lt;/strong&gt;——PH 卡付 PHP 金额在 Stripe 层面是&amp;quot;正常交易&amp;quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="三jp-注册的作用制造区域身份分裂"&gt;三、JP 注册的作用——制造&amp;quot;区域身份分裂&amp;quot;
&lt;/h2&gt;&lt;p&gt;你可能会问：为什么不直接在美国注册、美国提 Token、用 PH 卡付？&lt;/p&gt;
&lt;p&gt;答案是：&lt;strong&gt;直接美区注册的账号，定价系统的行为是确定性的——走 US 价格表，$20 USD，没有歧义可利用。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JP 注册的作用是制造一个&lt;strong&gt;区域身份不确定&lt;/strong&gt;的账号。当账号的 billing_country 是 JP，session IP 是 US，这两个信号已经矛盾了。定价系统在处理这种矛盾时，可能会：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;走入一个&lt;strong&gt;异常处理分支&lt;/strong&gt;，该分支的参数校验比正常路径更宽松&lt;/li&gt;
&lt;li&gt;允许第三方工具注入的额外参数（如 currency、discount）覆盖默认行为&lt;/li&gt;
&lt;li&gt;使用一个与主价格表不同的&lt;strong&gt;备用定价逻辑&lt;/strong&gt;，而这个逻辑有漏洞&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用状态机的视角看：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 注册时: JP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ locale=ja-JP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬───────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼───────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 登录时: US │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ip_country=US│
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ ≠ locale │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬───────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼───────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 创建 Checkout │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ JP ≠ US → 歧义 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 进入异常分支 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → 参数校验宽松 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────┬───────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ┌──────▼───────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ 工具注入参数 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ + Card BIN: PH │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; │ → amount = 0 PHP │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └──────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;三个不同的区域信号（JP / US / PH）制造了歧义，歧义打开了异常路径，异常路径上的校验缺失被利用。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是一个经典的安全反模式：当多个权威源对同一个问题给出不同答案时，系统在歧义中放松了校验——不是直接返回 0，而是&lt;strong&gt;给了攻击者操纵结果的空间&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四第三方提炼工具在干什么"&gt;四、第三方&amp;quot;提炼工具&amp;quot;在干什么？
&lt;/h2&gt;&lt;p&gt;流程里的那个第三方网站（&lt;code&gt;sms.linlinflow.ccwu.cc&lt;/code&gt;），名字叫&amp;quot;CDK 提炼&amp;quot;，听着玄乎，其实它干的事非常直白：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;接收你的 Access Token&lt;/li&gt;
&lt;li&gt;用这个 AT 调用 OpenAI 后端 API，创建一个 Stripe Checkout Session&lt;/li&gt;
&lt;li&gt;把 Checkout Session 的 URL 返回给你&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本质就是一个 &lt;strong&gt;AT → oaics_ 链接&lt;/strong&gt; 的代理服务。&lt;/p&gt;
&lt;p&gt;它的技术实现大概率是这样的：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 伪代码，非真实接口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://chatgpt.com/backend-api/payment/checkout&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`Bearer &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;plus&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// 关键：这里可能注入了额外参数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// billing_country? currency? locale?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;checkout_url&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// checkout_url = &amp;#34;https://chatgpt.com/checkout/openai_llc/oaics_...&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;关键问题：工具只是&amp;quot;转发&amp;quot;了 AT，还是&amp;quot;动了手脚&amp;quot;？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;考虑到 PH 区有明确的 Plus 定价（约 ₱990/月），而结果是 ₱0——&lt;strong&gt;工具大概率不只是简单转发&lt;/strong&gt;。它在创建 Checkout Session 时注入了额外参数，利用跨区账号的异常处理路径绕过了服务端校验。&lt;/p&gt;
&lt;p&gt;具体注入了什么参数？没有抓包数据我们无法确定。可能是 &lt;code&gt;promotion_code&lt;/code&gt;、&lt;code&gt;discount&lt;/code&gt;、&lt;code&gt;currency&lt;/code&gt; + &lt;code&gt;amount&lt;/code&gt; 覆盖、甚至是一个 OpenAI 内部的免费试用 API 路径。但有一点可以确定：&lt;strong&gt;如果工具只是忠实转发 AT，以 PH 区 ₱990 的定价，结果不可能是 ₱0。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;工具是这个攻击链中最不透明的环节。你把 Access Token——等同于你的登录态——交给了一个第三方服务。它用你的身份做了什么请求、传了什么参数，你完全不知道。这也是为什么整个流程的&amp;quot;技术含量&amp;quot;看似很低——&lt;strong&gt;核心逻辑都藏在工具服务端里&lt;/strong&gt;，用户只是按步骤操作。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="五bin-选择不是随便什么卡都行"&gt;五、BIN 选择——不是随便什么卡都行
&lt;/h2&gt;&lt;p&gt;流传的步骤里特别强调了两个 BIN：&lt;code&gt;523686&lt;/code&gt;（Mastercard）和 &lt;code&gt;4513&lt;/code&gt;（Visa）。这不是随便挑的。&lt;/p&gt;
&lt;h3 id="bin-在支付流中的角色"&gt;BIN 在支付流中的角色
&lt;/h3&gt;&lt;p&gt;BIN（Bank Identification Number）是卡号的前 6-8 位，编码了发卡行、卡网络、卡类型和发行国等信息。Stripe 在收到卡号的前 6 位时就能确定：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;523686 → Mastercard / Philippines / Credit / 某发卡行
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个信息会被写入 &lt;code&gt;payment_method.card.country&lt;/code&gt;，并在整个支付流中传播。&lt;/p&gt;
&lt;h3 id="为什么必须是-ph-bin"&gt;为什么必须是 PH BIN？
&lt;/h3&gt;&lt;p&gt;PH BIN 的作用不是&amp;quot;触发一个空的价格表&amp;quot;——PH 价格表是存在的（约 ₱990/月）。&lt;/p&gt;
&lt;p&gt;更可能的解释是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;货币匹配&lt;/strong&gt;：如果工具创建的 Checkout Session 指定了 &lt;code&gt;currency: &amp;quot;php&amp;quot;&lt;/code&gt;，那么 PH 发行的卡才能让 Stripe 的 currency-card 一致性检查通过。US 卡 + PHP 货币会触发额外的风控审查。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;风控绕过&lt;/strong&gt;：OpenAI/Stripe 的风控系统可能对&amp;quot;card country = PH + checkout currency = PHP&amp;quot;这个组合判定为&amp;quot;正常的本地交易&amp;quot;，不会触发跨境支付的额外校验。而如果用 JP 卡或 US 卡来付一笔 0 PHP 的订单，风控更容易拦截。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AVS 绕过&lt;/strong&gt;：PH 卡不走 Stripe 的 AVS 地址校验，所以填什么地址都行——这降低了被拒的概率。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;523686 和 4513 是经过实测确认能被 Stripe 正确识别为 &lt;code&gt;card.country = &amp;quot;PH&amp;quot;&lt;/code&gt; 的 BIN 段。不是所有 PH BIN 都行，因为某些 BIN 可能已被 OpenAI/Stripe 的风控规则标记或黑名单。&lt;/p&gt;
&lt;h3 id="为什么要填美国账单地址"&gt;为什么要填美国账单地址？
&lt;/h3&gt;&lt;p&gt;这步看起来矛盾——卡是菲律宾的，地址填美国？&lt;/p&gt;
&lt;p&gt;原因是 Stripe 的 AVS（Address Verification Service）只对美国和英国的卡做地址校验。PH 卡不走 AVS，所以你填什么地址都无所谓——但如果你填 PH 地址，可能触发 OpenAI 的额外风控逻辑。填 US 地址是为了&amp;quot;看起来正常&amp;quot;，降低被风控拦截的概率。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="六从防御视角看openai-需要修什么"&gt;六、从防御视角看——OpenAI 需要修什么
&lt;/h2&gt;&lt;p&gt;不管具体是哪种假说成立，防御原则是通用的。&lt;/p&gt;
&lt;h3 id="修复方案从简单到全面"&gt;修复方案（从简单到全面）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Level 1：Checkout Session 金额校验&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_checkout_session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolve_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;amount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;free&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Zero-amount checkout for paid plan&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;付费产品金额为 0 时直接拒绝创建 session。这应该是支付系统的基本不变量。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 3：区域一致性校验&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;resolve_country&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;registration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;billing_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# JP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;session_ip&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;geoip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_ip&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;# US&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;card_bin&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 创建 session 时还不知道&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 如果注册地和 session IP 不一致，以注册地为准&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 不让 card BIN 覆盖定价区域&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;signals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;registration&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;定价的 country 由一个确定性函数决定，不受 card BIN 影响。Card BIN 的 country 只用于风控信号，不参与定价。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Level 4：Webhook 后置校验&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_checkout_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount_total&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;subscription&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 0 元订阅？不对劲&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;stripe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Zero-amount subscription detected&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即使 Checkout Session 创建时金额为 0，在 &lt;code&gt;checkout.session.completed&lt;/code&gt; webhook 里也要做最终校验。这是最后一道防线。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七这个漏洞和之前的几个有什么关系"&gt;七、这个漏洞和之前的几个有什么关系？
&lt;/h2&gt;&lt;p&gt;放在一起看，这几个月被扒出来的 ChatGPT/Claude 支付漏洞其实形成了一个谱系：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;漏洞&lt;/th&gt;
 &lt;th&gt;攻击层&lt;/th&gt;
 &lt;th&gt;改了什么&lt;/th&gt;
 &lt;th&gt;核心缺陷&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;prorationMode Hook&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;客户端内存&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;putInt&lt;/code&gt; 的一个参数&lt;/td&gt;
 &lt;td&gt;Google Play 信任客户端传入的升级策略&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/" target="_blank" rel="noopener"
 &gt;焚决 (cassia)&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;HTTP 响应&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;checkout_flow&lt;/code&gt; 字段&lt;/td&gt;
 &lt;td&gt;前端根据 API 响应选择支付通道，后端不校验&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;RevenueCat 转移&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;聚合器 API&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;is_restore&lt;/code&gt; + &lt;code&gt;app_user_id&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;匿名购买 + restore 归属无限转移&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;API 响应注入&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;HTTP 响应&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;eligible_promo_campaigns&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;优惠码资格校验仅在前端&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;0 PHP 跨区&lt;/strong&gt; (本文)&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;定价系统&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;区域信号 + 工具注入&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;跨区歧义打开异常路径 + checkout 参数校验缺失&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;注意到了吗？攻击层在不断&amp;quot;上移&amp;quot;。&lt;/p&gt;
&lt;p&gt;早期的漏洞需要 Root、需要 Frida、需要改代码——门槛不低。焚决降到了一个油猴脚本的水平。而 0 PHP 攻击&lt;strong&gt;连脚本都不需要&lt;/strong&gt;——纯操作流程，任何人都能复现。&lt;/p&gt;
&lt;p&gt;从攻击者的角度看，这是一个自然演进：&lt;strong&gt;当客户端层的防御越来越强（代码混淆、完整性校验、证书绑定），攻击者就会转向更高层——业务逻辑层、定价系统层、区域路由层。&lt;/strong&gt; 这些层的防御往往更薄弱，因为它们不像&amp;quot;加密&amp;quot;“签名&amp;quot;那样有明确的安全框架，它们是业务系统的一部分，安全评审时容易被忽略。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="八更通用的攻击模式区域信号碎片化"&gt;八、更通用的攻击模式——“区域信号碎片化”
&lt;/h2&gt;&lt;p&gt;0 PHP 攻击其实揭示了一个通用的攻击模式，我把它叫做**“区域信号碎片化攻击”（Region Signal Fragmentation）**。&lt;/p&gt;
&lt;h3 id="攻击模型"&gt;攻击模型
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;victim_system&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pricing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;registration_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（注册时选择&lt;/span&gt; &lt;span class="n"&gt;VPN&lt;/span&gt; &lt;span class="err"&gt;节点）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;session_ip_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（登录时选择&lt;/span&gt; &lt;span class="n"&gt;VPN&lt;/span&gt; &lt;span class="err"&gt;节点）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;card_bin_country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（选择特定&lt;/span&gt; &lt;span class="n"&gt;BIN&lt;/span&gt; &lt;span class="err"&gt;的卡）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;browser_locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（浏览器设置）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;signal_5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;billing_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="err"&gt;攻击者控制（表单填写）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;所有五个信号都由攻击者完全控制。如果 &lt;code&gt;resolve()&lt;/code&gt; 函数对歧义输入的处理不当——比如进入异常分支后放松了参数校验，或者 fallback 逻辑允许客户端覆盖定价——攻击者就能把定价推入非预期状态。&lt;/p&gt;
&lt;h3 id="防御原则"&gt;防御原则
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;原则一：定价权归一&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pricing_country = user.billing_country // 只有一个来源，注册时确定
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// card BIN、IP、locale 都不参与定价决策
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// 只作为风控辅助信号
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;原则二：Checkout 参数不可客户端覆盖&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 服务端决定 price、currency、amount
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 客户端/第三方传入的 discount、coupon、price_override 一律忽略
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 只接受服务端白名单内的 promotion_code，且校验账号资格
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;checkout_session = stripe.checkout.Session.create(
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; line_items=[server_determined_price], # 不接受客户端 price_data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; discounts=[], # 不接受客户端 coupon
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;客户端能影响定价 = 攻击面。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原则三：零元红线&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;if checkout.amount == 0 and plan.type == &amp;#34;paid&amp;#34;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; REJECT() // 无条件拒绝
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;付费产品不可能合法地出现 0 元。这应该是一个硬编码的不变量，不依赖任何业务逻辑。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="九写在最后"&gt;九、写在最后
&lt;/h2&gt;&lt;p&gt;从 prorationMode 到焚决到 RevenueCat 到 0 PHP，每一个漏洞都指向同一个结构性问题：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;当支付系统由多个独立组件（商店、聚合器、定价引擎、checkout 前端、webhook 后端）拼装而成时，组件之间的信任传递就是攻击面。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;prorationMode 的信任传递是&amp;quot;Google Play 信任客户端传入的升级策略”。焚决的信任传递是&amp;quot;前端信任 API 返回的支付通道&amp;quot;。RevenueCat 的信任传递是&amp;quot;聚合器信任 SDK 传入的 app_user_id&amp;quot;。0 PHP 的信任传递是&amp;quot;定价系统信任 card BIN 推断的国家&amp;quot;。&lt;/p&gt;
&lt;p&gt;没有一个单点漏洞，都是&lt;strong&gt;链路上的信任滑坡&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而防御的核心思路也始终一样：&lt;strong&gt;在链路的最末端（服务端 webhook、权益发放点）做最终校验，不信任链路上游传递的任何&amp;quot;结论&amp;quot;，只信任可验证的&amp;quot;事实&amp;quot;。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;具体到 0 PHP 这个 case：Stripe &lt;code&gt;checkout.session.completed&lt;/code&gt; 事件里的 &lt;code&gt;amount_total&lt;/code&gt; 是事实，&lt;code&gt;currency&lt;/code&gt; 是事实。如果一个 ChatGPT Plus 订阅的 &lt;code&gt;amount_total&lt;/code&gt; 是 0——不管前面经历了多少层区域路由和定价查询——&lt;strong&gt;这就不对&lt;/strong&gt;。拦住它。&lt;/p&gt;
&lt;p&gt;这比修价格表更重要。因为价格表总有遗漏，但&amp;quot;付费产品不能 0 元&amp;quot;这条规则没有例外。&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;往期相关&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/google-play-ccmax-proration-vulnerability-2026/" target="_blank" rel="noopener"
 &gt;Google Play 内购 CC Max 漏洞深度拆解&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/claude-cassia-mock-payment-bypass-analysis/" target="_blank" rel="noopener"
 &gt;焚决 Claude — 油猴脚本撬开支付大门&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-exploit-revenuecat-vulnerability/" target="_blank" rel="noopener"
 &gt;GPT Plus 订阅漏洞 — RevenueCat 凭证转移&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.7qiang.com/posts/gpt-plus-google-play-subscription-transfer/" target="_blank" rel="noopener"
 &gt;GPT Plus 订阅漏洞 — Google Play Billing 鉴权缺失&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

 &lt;/blockquote&gt;</description></item><item><title>newapi Stripe Webhook 充值漏洞深度分析 - 空密钥绕过支付验证</title><link>https://blog.7qiang.com/posts/newapi-stripe-webhook-vulnerability-analysis/</link><pubDate>Thu, 16 Apr 2026 05:07:00 +0000</pubDate><guid>https://blog.7qiang.com/posts/newapi-stripe-webhook-vulnerability-analysis/</guid><description>&lt;img src="https://blog.7qiang.com/blog-cover-cyber-mask.gif" alt="Featured image of post newapi Stripe Webhook 充值漏洞深度分析 - 空密钥绕过支付验证" /&gt;&lt;section class="xiaoyao-contact-card" aria-label="联系逍遥"&gt;
 &lt;img class="xiaoyao-contact-card__qr" src="https://blog.7qiang.com/contact-qr.png" width="690" height="690" loading="lazy" decoding="async" alt="逍遥微信二维码"&gt;
 &lt;div class="xiaoyao-contact-card__content"&gt;
 &lt;h3 class="xiaoyao-contact-card__title"&gt;联系逍遥&lt;/h3&gt;
 &lt;p class="xiaoyao-contact-card__description"&gt;扫码添加微信，或点击下方按钮复制联系方式。&lt;/p&gt;
 &lt;div class="xiaoyao-contact-buttons" data-contact-buttons&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--wechat" type="button" data-copy-contact="plus6566" data-copy-label="微信号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;微信&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;plus6566&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;button class="xiaoyao-contact-button xiaoyao-contact-button--qq" type="button" data-copy-contact="826215906" data-copy-label="QQ 号"&gt;
 &lt;span class="xiaoyao-contact-button__label"&gt;QQ&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__value"&gt;826215906&lt;/span&gt;
 &lt;span class="xiaoyao-contact-button__status" data-copy-status&gt;点击复制&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="xiaoyao-contact-feedback" role="status" aria-live="polite"&gt;&lt;/span&gt;
&lt;/div&gt;

 &lt;/div&gt;
&lt;/section&gt;

&lt;p&gt;进微信群请联系博主，各位觉得文章对你有帮助的话可否打赏一些呀~&lt;/p&gt;
&lt;hr&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 时效性提醒（2026-07 更新）&lt;/strong&gt;：该漏洞已在 newapi v0.12.10 中修复。如果你正在使用 newapi，请确认已升级到修复版本。本文保留仅供支付安全设计参考。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="漏洞概述"&gt;漏洞概述
&lt;/h2&gt;&lt;p&gt;2026 年 4 月 16 日，LINUX DO 社区用户披露了 newapi 项目中的一个严重充值漏洞。攻击者可在未配置 Stripe 密钥的情况下，通过伪造签名绕过支付验证，实现&lt;strong&gt;零成本任意金额充值&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;影响版本&lt;/strong&gt;：v0.12.10 之前
&lt;strong&gt;修复版本&lt;/strong&gt;：v0.12.10
&lt;strong&gt;漏洞类型&lt;/strong&gt;：签名验证绕过 / 支付逻辑缺陷
&lt;strong&gt;风险等级&lt;/strong&gt;：🔴 严重（直接资金损失）&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="攻击流程详解"&gt;攻击流程详解
&lt;/h2&gt;&lt;h3 id="第一阶段信息收集"&gt;第一阶段：信息收集
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1. 发现目标暴露了 Stripe Webhook 端点 /api/stripe/webhook
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2. 通过初始审计（或黑盒探测）确认服务端使用了空的 StripeWebhookSecret
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3. 获取或猜测 client_reference_id 的格式（如 USR + 数字 + 随机字符串 + 时间戳）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;攻击者首先识别出目标的 Webhook 端点，并探测到服务端未正确配置 &lt;code&gt;webhook_secret&lt;/code&gt;（空字符串）。这是漏洞利用的关键前提。&lt;/p&gt;
&lt;h3 id="第二阶段伪造签名"&gt;第二阶段：伪造签名
&lt;/h3&gt;&lt;p&gt;Stripe 的签名机制：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;signed_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{timestamp}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{json_body}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HMAC&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;SHA256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webhook_secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;signed_payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;t=&lt;/span&gt;&lt;span class="si"&gt;{timestamp}&lt;/span&gt;&lt;span class="s2"&gt;,v1=&lt;/span&gt;&lt;span class="si"&gt;{v1}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;当 webhook_secret = “&amp;quot;（空字符串）时：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HMAC-SHA256 不会报错，正常计算&lt;/li&gt;
&lt;li&gt;任何人都能对任意 payload 算出与服务端一致的签名&lt;/li&gt;
&lt;li&gt;服务端调用 &lt;code&gt;webhook.ConstructEventWithOptions()&lt;/code&gt; 验签 → &lt;strong&gt;通过 ✅&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是整个漏洞的核心：&lt;strong&gt;空密钥仍能通过 HMAC 计算，导致签名验证形同虚设&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="第三阶段构造恶意事件"&gt;第三阶段：构造恶意事件
&lt;/h3&gt;&lt;p&gt;伪造一个 &lt;code&gt;checkout.session.completed&lt;/code&gt; 事件：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;checkout.session.completed&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;data&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;client_reference_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;目标用户/订单 ID&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;complete&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;payment_status&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;paid&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;amount_total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;任意金额&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;攻击者可指定任意 &lt;code&gt;client_reference_id&lt;/code&gt; 和 &lt;code&gt;amount_total&lt;/code&gt;，实现定向充值和任意金额。&lt;/p&gt;
&lt;h3 id="第四阶段发送请求"&gt;第四阶段：发送请求
&lt;/h3&gt;&lt;p&gt;向 Webhook 端点发送带有伪造签名的 POST 请求，服务端处理流程：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;收到请求
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;用空密钥验签 → 通过 ✅
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;解析事件类型 → checkout.session.completed
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;读取 client_reference_id 查找订单
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;标记订单已支付，给用户充值
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第五阶段结果"&gt;第五阶段：结果
&lt;/h3&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;项目&lt;/th&gt;
 &lt;th&gt;说明&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;用户余额&lt;/td&gt;
 &lt;td&gt;被充入攻击者指定的金额&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Stripe 实际收款&lt;/td&gt;
 &lt;td&gt;&lt;strong&gt;$0&lt;/strong&gt;（从未发生真实支付）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Stripe Dashboard&lt;/td&gt;
 &lt;td&gt;无对应交易记录&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;服务端日志&lt;/td&gt;
 &lt;td&gt;看起来像正常的 Webhook 回调&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="技术原理分析"&gt;技术原理分析
&lt;/h2&gt;&lt;h3 id="为什么空密钥能通过验签"&gt;为什么空密钥能通过验签？
&lt;/h3&gt;&lt;p&gt;HMAC-SHA256 算法本身&lt;strong&gt;不验证密钥长度&lt;/strong&gt;。当密钥为空字符串时：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hmac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;hashlib&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 空密钥仍能计算&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# 空密钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;timestamp.body&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 结果：有效的 64 字符十六进制字符串&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;服务端代码若未检查 &lt;code&gt;webhook_secret&lt;/code&gt; 是否为空，就会用空密钥验签，导致攻击者可以：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;获取相同的 payload&lt;/li&gt;
&lt;li&gt;用空密钥计算相同的签名&lt;/li&gt;
&lt;li&gt;伪造出&amp;quot;合法&amp;quot;的请求&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="正确的防御姿势"&gt;正确的防御姿势
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ❌ 错误：未检查密钥是否为空&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;webhook_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;webhook_secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span class="c1"&gt;# 危险！&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ✅ 正确：强制要求配置密钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;webhook_secret&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;webhook_secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;ConfigurationError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Stripe webhook secret must be configured&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="影响范围"&gt;影响范围
&lt;/h2&gt;&lt;h3 id="直接受影响"&gt;直接受影响
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;所有使用 newapi v0.12.10 之前版本&lt;/li&gt;
&lt;li&gt;未配置或错误配置 &lt;code&gt;StripeWebhookSecret&lt;/code&gt; 的实例&lt;/li&gt;
&lt;li&gt;依赖 Stripe Webhook 进行充值确认的业务&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="潜在风险"&gt;潜在风险
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;资金损失&lt;/strong&gt;：攻击者可零成本获取服务额度&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据污染&lt;/strong&gt;：虚假交易记录污染财务数据&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;合规风险&lt;/strong&gt;：支付审计无法通过&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="修复建议"&gt;修复建议
&lt;/h2&gt;&lt;h3 id="立即措施"&gt;立即措施
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;升级版本&lt;/strong&gt;：立即升级到 v0.12.10 或更高版本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;检查配置&lt;/strong&gt;：确认 &lt;code&gt;StripeWebhookSecret&lt;/code&gt; 已正确配置（非空）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;审计日志&lt;/strong&gt;：检查历史 Webhook 记录，排查异常充值&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="长期加固"&gt;长期加固
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;启动检查&lt;/strong&gt;：服务启动时强制验证关键配置项&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;签名验证增强&lt;/strong&gt;：除 HMAC 外，增加时间戳窗口校验&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;对账机制&lt;/strong&gt;：定期与 Stripe Dashboard 对账，发现不一致立即告警&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;监控告警&lt;/strong&gt;：对大额充值、频繁充值设置阈值告警&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="代码修复示例"&gt;代码修复示例
&lt;/h2&gt;&lt;p&gt;v0.12.10 的修复逻辑（推测）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 修复前&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;VerifyWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StripeWebhookSecret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 可能为空&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConstructEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 修复后&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;VerifyWebhook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StripeWebhookSecret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;stripe webhook secret not configured&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConstructEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StripeWebhookSecret&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="总结"&gt;总结
&lt;/h2&gt;&lt;p&gt;这是一个典型的&lt;strong&gt;配置缺陷 + 逻辑漏洞&lt;/strong&gt;组合：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;配置缺陷&lt;/strong&gt;：允许空密钥存在&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;逻辑漏洞&lt;/strong&gt;：未验证密钥有效性即进行签名计算&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;教训&lt;/strong&gt;：安全配置项必须在上层进行有效性校验，不能依赖底层库的&amp;quot;隐式行为”。空字符串不是&amp;quot;未配置&amp;quot;，而是&amp;quot;配置了空值&amp;quot;——这两者有本质区别。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="参考资料"&gt;参考资料
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/QuantumNous/new-api/releases/tag/v0.12.10" target="_blank" rel="noopener"
 &gt;new-api v0.12.10 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://stripe.com/docs/webhooks/best-practices" target="_blank" rel="noopener"
 &gt;Stripe Webhook Security Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;本文基于社区披露信息进行分析，仅供安全研究参考。请合法合规使用，未经授权不得对任何系统进行测试。&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>