This commit is contained in:
dela
2026-01-29 20:02:02 +08:00
parent 82f7314922
commit d60327e588
10 changed files with 545 additions and 2037 deletions

280
core/checkout.py Normal file
View File

@@ -0,0 +1,280 @@
#!/usr/bin/env python3
"""
ChatGPT Plus 支付账单获取模块
功能:
- 使用 access_token 自动获取 Stripe 支付会话
- 返回 checkout_session_id 和 client_secret 用于后续支付
使用方法:
from core.checkout import CheckoutFlow
session = OAISession(proxy=proxy)
await session.login(email, password)
checkout = CheckoutFlow(session)
result = await checkout.create_checkout_session()
"""
from typing import Dict, Any, Optional
from utils.logger import logger
class CheckoutFlow:
"""
ChatGPT Plus 支付结账流程
通过 /backend-api/payments/checkout 接口获取 Stripe 支付会话
"""
# API 端点
CHECKOUT_URL = "https://chatgpt.com/backend-api/payments/checkout"
# 支持的订阅计划
PLAN_PLUS = "chatgptplusplan"
PLAN_PRO = "chatgptproplan"
PLAN_TEAM = "chatgptteamplan"
def __init__(
self,
session,
plan_name: str = "chatgptplusplan",
country: str = "US",
currency: str = "USD"
):
"""
初始化结账流程
参数:
session: OAISession 实例(需已登录)
plan_name: 订阅计划名称
- chatgptplusplan: ChatGPT Plus ($20/月)
- chatgptproplan: ChatGPT Pro ($200/月)
country: 账单国家代码
currency: 货币代码
"""
self.session = session
self.plan_name = plan_name
self.country = country
self.currency = currency
# 验证登录状态
if not session.is_logged_in():
logger.warning("Session not logged in - checkout may fail")
async def create_checkout_session(
self,
checkout_ui_mode: str = "redirect"
) -> Dict[str, Any]:
"""
创建 Stripe 结账会话
参数:
checkout_ui_mode: 结账UI模式
- "redirect": 重定向到 Stripe 托管页面
- "custom": 使用自定义嵌入式表单
返回:
成功时返回:
{
"status": "success",
"checkout_session_id": "cs_live_xxx",
"client_secret": "cs_live_xxx_secret_xxx",
"publishable_key": "pk_live_xxx",
"plan_name": "chatgptplusplan",
"payment_status": "unpaid",
"url": "https://checkout.stripe.com/..." (如果 ui_mode=redirect)
}
失败时返回:
{
"status": "failed",
"error": "错误信息",
"status_code": HTTP状态码
}
"""
logger.info(f"Creating checkout session for plan: {self.plan_name}")
# 构建请求体
payload = {
"plan_name": self.plan_name,
"billing_details": {
"country": self.country,
"currency": self.currency
},
"checkout_ui_mode": checkout_ui_mode
}
# 构建请求头
headers = self._build_headers()
try:
response = self.session.post(
self.CHECKOUT_URL,
json=payload,
headers=headers
)
return self._parse_response(response)
except Exception as e:
logger.error(f"Checkout request failed: {e}")
return {
"status": "failed",
"error": str(e),
"message": f"Request exception: {type(e).__name__}"
}
def _build_headers(self) -> Dict[str, str]:
"""
构建结账请求头
包含必要的 Authorization 和 OpenAI 特定头
"""
headers = {
"Content-Type": "application/json",
"Accept": "*/*",
"Origin": "https://chatgpt.com",
"Referer": "https://chatgpt.com/",
"Oai-Language": "en-US",
"Oai-Device-Id": self.session.oai_did,
}
# 添加 Authorization 头Bearer token
if self.session.access_token:
headers["Authorization"] = f"Bearer {self.session.access_token}"
else:
logger.warning("No access_token available for Authorization header")
return headers
def _parse_response(self, response) -> Dict[str, Any]:
"""
解析结账响应
参数:
response: HTTP 响应对象
返回:
解析后的结果字典
"""
status_code = response.status_code
if status_code == 200:
try:
data = response.json()
result = {
"status": "success",
"tag": data.get("tag"),
"checkout_session_id": data.get("checkout_session_id"),
"client_secret": data.get("client_secret"),
"publishable_key": data.get("publishable_key"),
"processor_entity": data.get("processor_entity"),
"checkout_ui_mode": data.get("checkout_ui_mode"),
"plan_name": data.get("plan_name"),
"payment_status": data.get("payment_status"),
"url": data.get("url"),
"automatic_tax_enabled": data.get("automatic_tax_enabled"),
"requires_manual_approval": data.get("requires_manual_approval"),
}
logger.success(f"Checkout session created: {result['checkout_session_id'][:30]}...")
return result
except Exception as e:
logger.error(f"Failed to parse checkout response: {e}")
return {
"status": "failed",
"error": f"JSON parse error: {e}",
"raw_response": response.text[:500]
}
elif status_code == 401:
logger.error("Unauthorized - invalid or expired token")
return {
"status": "failed",
"error": "Unauthorized - token invalid or expired",
"status_code": 401
}
elif status_code == 403:
logger.error("Forbidden - possible Cloudflare block or permission issue")
return {
"status": "failed",
"error": "Forbidden - access denied",
"status_code": 403,
"raw_response": response.text[:500]
}
elif status_code == 400:
logger.error(f"Bad request: {response.text[:200]}")
return {
"status": "failed",
"error": "Bad request - invalid parameters",
"status_code": 400,
"raw_response": response.text[:500]
}
else:
logger.error(f"Unexpected status {status_code}: {response.text[:200]}")
return {
"status": "failed",
"error": f"HTTP {status_code}",
"status_code": status_code,
"raw_response": response.text[:500]
}
async def get_checkout_for_account(
email: str,
password: str,
proxy: str = None,
plan_name: str = "chatgptplusplan"
) -> Dict[str, Any]:
"""
便捷函数:为指定账号获取支付链接
参数:
email: 账号邮箱
password: 账号密码
proxy: 代理地址(可选)
plan_name: 订阅计划
返回:
包含 checkout 信息的字典
"""
from core.session import OAISession
session = None
try:
session = OAISession(proxy=proxy, impersonate="chrome124")
# 登录
logger.info(f"Logging in as {email}...")
login_result = await session.login(email, password)
if login_result.get("status") != "success":
return {
"status": "failed",
"error": f"Login failed: {login_result.get('error')}",
"email": email
}
# 获取 checkout
checkout = CheckoutFlow(session, plan_name=plan_name)
result = await checkout.create_checkout_session()
result["email"] = email
return result
finally:
if session:
session.close()
# 导出
__all__ = [
"CheckoutFlow",
"get_checkout_for_account",
]