This post is a follow-up documenting the process of successfully using a direct-issued Checkout card with a ChatGPT discount around August 2026. At the time, I used the approach of “linking the card first, then creating the Checkout” to address the issue where certain card issuers were prone to rejecting transactions when submitted directly on the checkout page.
Statement : This article draws on existing projects from the open-source community. My contribution primarily involved integrating the order creation and automatic payment processes. Since the relevant APIs and risk control rules are subject to change at any time, this document is intended solely for protocol research and troubleshooting purposes; please do not use it for any illegal activities.
Nowadays, risk control for direct card payments is increasingly focused on the card issuer, so simply binding a card first may not necessarily work. Here, I’m documenting the protocol flow from that time and the actual pitfalls I encountered.
1. Why is it necessary to “link your card first”?
The standard human-machine checkout process is as follows: The user opens a web page, navigates to the checkout screen, enters their card number in the embedded Stripe Elements form, and submits it. However, for certain card issuers (such as those used for testing, 4513 (Cardholder) If you try to pay for a discount voucher directly on the checkout page, the transaction will almost certainly fail.
Through trial and error, the community discovered a way to decouple the process: for the same card, instead of entering it directly on the checkout page, users first go through a separate card-binding process to “attach” it to their account, and then proceed to the “Checkout” page to redeem the discount using the card that has already been saved. pm_(PaymentMethod), and the payment will go through.
flowchart LR
subgraph Direct ["直接结账路径 (易被拒)"]
D1["优惠 Checkout 页面"] --> D2["Stripe Elements 输入卡号"] --> D3["直接扣款风控拦截 ❌"]
end
subgraph Split ["解耦路径 (先绑后结)"]
S1["SetupIntent 独立绑卡"] --> S2["Stripe 校验卡片生成 pm_"] --> S3["Attach 到账号作为默认卡"]
S3 --> S4["创建优惠 Checkout"] --> S5["消费已有 pm_ 完成确认 ✔️"]
end
The verification processes for the two entry points are different:
- Checkout Now : When a new card is entered on the Checkout page, Stripe Radar and the platform perform a real-time fraud assessment based on the instant authorization policy and the user’s input behavior;
- Pre-binding : Through
/backend-api/payments/payment_methodInitiate the binding via StripeSetupIntent, used to verify the card’s basic validity and authorize subsequent transactions (Off-Session Setup). The validation rules differ between the two processes, and the corresponding merchant shard keys (such as those commonly used by OpenAI)KslHRdbaPgandC6h1nxGoI3) may also differ.
During implementation, the original sequence of actions needs to be broken down into two stages: “independent card binding” and “paying with a previously linked card.”
2. Account sessions and context isolation
When running automation, the input is typically a valid Access Token (AT). The program uses the AT to parse information such as the Account ID and establishes a separate session context for each execution unit.
Account Context
├── Access Token (用于调用业务 API)
├── Account ID (标识计费归属)
├── Device ID (模拟客户端设备)
└── Session Cookies (维护身份与防爬会话)
Failure to properly isolate contexts is the most common cause of issues. Both Stripe and the platform API verify the ownership of IP addresses, cookies, and sessions. If Account A’s device ID or cookie ends up in a request from Account B, the consequences range from simple errors indicating that the state cannot be found to, in the worst case, triggering risk control measures.
When running multiple accounts in bulk, operations can be performed in parallel; however, for a single account, the internal steps of binding a card and checkout must be executed serially with locking.
3. Card Registration Process and Practical Issues
The goal of pre-binding a card is to get Stripe to generate a valid pm_... and link it to your current account.
3.1 Protocol Flow Paths
- Client Request to Access Platform APIs
SetupIntent, getclient_secretand the corresponding Stripe Publishable Key (pk_live_...); - Load Stripe Elements in a browser or headless environment, and submit the card number, expiration date, and CVC directly to Stripe;
- Adjust
stripe.confirmCardSetup, with Stripe handling the verification and generating the PaymentMethod ID; - The client notification platform will send this
pm_Set as the default payment method.
sequenceDiagram
participant Client as 自动化客户端
participant OAI as 平台 API (ChatGPT)
participant Stripe as Stripe 网关
Client->>OAI: POST /payments/payment_method (申请 SetupIntent)
OAI-->>Client: 返回 client_secret 与 pk_live
Client->>Stripe: confirmCardSetup (卡号/CVC 提交)
Stripe-->>Client: 返回成功并生成 pm_xxxx
Client->>OAI: 设置默认付款方式 (Attach)
OAI-->>Client: 确认绑定指令已接收
3.2 Three Practical Issues
When implementing this logic as an automated process, there are three very specific engineering pitfalls:
Missing Publishable Key
For some new accounts, when they request `SetupIntent` for the first time, the API returns only the `Intent` itself, without providing the `Publishable Key` required by the rendering component. You can first run a lightweight Checkout initialization within the same account session, then return to read the context. You cannot use another account’s public key.
Concurrency Conflicts When Reusing UI Components
When processing multiple accounts in bulk, although the server-side `SetupIntent` can be prepared in parallel, there is typically only one `Card Element` on the front-end page. If multiple tasks are called simultaneously, confirmCardSetup... will result in an “in-flight” conflict.
The server can prepare each SetupIntent in parallel; when the front end reaches the card-entry stage, it uses a single queue to confirm them one by one. Once a card is successfully confirmed, the subsequent Attach requests are sent in parallel.
Stripe Confirms Delay with the Attach Platform
stripe.confirmCardSetup A successful response simply indicates that Stripe has been set up. pm_...the platform’s own billing system may not be able to locate it yet.
After configuring “Set Default Payment Method,” you need to poll the platform’s saved_payment_methods Interface. Objective pm_ Add the card to the list before creating a checkout; otherwise, the card may appear as successfully linked but won’t be visible on the billing statement for the time being.
4. Integrating Discounts with Checkout and Amount-Based Access Control
The “Checkout” offer is created only after the card has been successfully “Attached.” The term “extraction” here refers to the process of converting the account’s existing promotional eligibility into a temporary order with a clearly defined status that can be verified via an API call.
flowchart TD
A["已完成 Attach 的账号"] --> B["创建优惠 Checkout (NL/US等)"]
B --> C["应用 Promotion / 折扣代码"]
C --> D["重新读取 Checkout Snapshot"]
D --> E{"金额与币种门禁校验"}
E -- 异常/非预期扣费 --> F["硬终止并告警 🛑"]
E -- 符合预期 (如 0 元试用) --> G["读取当前 Customer Context"]
G --> H["校验 pm_ 归属并生成 ConfirmationToken"]
H --> I["确认支付 (Confirm)"]
Amount Verification
To prevent charges at the original price (e.g., $20) after the promotion expires, please check the amount before confirming:
- A 200 response from the Promotion API does not necessarily mean the discount has actually taken effect;
- You must reread the section in “Checkout Snapshot” titled
amount_totalandcurrency; - If the goal is a free trial, the amount due must be 0; if the amount is incorrect or displays the regular subscription price, terminate the task immediately.
5. Verifying the PaymentMethod Before Payment
Before finalizing the payment deduction, a two-way verification must be performed:
- At the account level
saved_payment_methodsList; - Currently mounted within the Checkout instance
Customer ContextPayment Methods.
Both sides must include the one that was just tied pm_...in order to continue configuring Stripe for creation ConfirmationToken(ctoken_...), and finally adjust the platform’s /confirm Submit via the API.
Saved PaymentMethod (账号侧)
↓
Customer Context (订单侧一致性核验)
↓
Stripe ConfirmationToken (生成临时确认凭证)
↓
ChatGPT Checkout Confirm (平台发起终审)
↓
Stripe PaymentIntent / SetupIntent (网关完成状态结算)
For zero-yuan trial orders, the underlying confirmation is essentially SetupIntent; If you encounter requires_action(If further challenge verification is required), the program must capture the challenge and forward it to the appropriate verification module; it should not blindly assume success simply because the API returns a 200 status code.
6. How to Determine Success, and When to Retry
Progress along the entire pipeline must be assessed based on the actual status at each step; it is not enough to simply check whether a request returned an error:
| Stage | Inspection Items | Error Handling |
|---|---|---|
| Card Verification | Received the valid response from Stripe pm_ | The “Card Rejected” message indicates that the card has expired; discard the task. |
| Attach | Account saved_payment_methods Looked up the target ID | Short-cycle backoff polling; if a timeout occurs, the session is reset |
| Deal Apps | In the Checkout Snapshot amount_total Meet the Standards | If the amount is incorrect, immediately cancel the transaction and exit; deductions are strictly prohibited. |
| Order Links | Checkout: Saw the same one pm_ | Reinitialize the Checkout Context |
| Final State Confirmation | The order status changes to “Active,” or the “Intent” is succeeded | Handling 3DS / hCaptcha or Tagging Failures |
If there are network fluctuations or temporary timeouts, you can retry the current step. If the amount is incorrect, the session has expired, or the IDs do not match, you should clear the context for this round and start over; do not carry over data from the previous round. pm_ Or carry the cookie over to the next round.
At the time, the process we followed was to first save the card, wait for the “Attach” operation to complete, and then create and confirm the “Checkout.” Each step required checking the corresponding status, which made it relatively easy to pinpoint the source of any errors.
iDEAL does not require a card to be linked in advance; the second part of the process switches to a Provider Redirect, which is recorded separately inNext Article。

