| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // background/checkout-api-utils.js — API helpers for Stripe/PayPal checkout automation
- (function attachCheckoutApiUtils(root) {
- root.MultiPageCheckoutApiUtils = (function createCheckoutApiUtilsModule() {
- const ADDRESS_API_URL = 'https://www.meiguodizhi.com/api/v1/dz';
- const CHATGPT_CHECKOUT_URL = 'https://chatgpt.com/backend-api/payments/checkout';
- const DEFAULT_PAYPAL_SMS_MAX_ATTEMPTS = 18;
- const DEFAULT_PAYPAL_SMS_INTERVAL_MS = 5000;
- /**
- * Fetch a random US address from meiguodizhi.com.
- * @returns {Promise<{street: string, city: string, state: string, zip: string}>}
- */
- async function fetchRandomAddress() {
- try {
- const response = await fetch(ADDRESS_API_URL, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ path: '/', method: 'address' }),
- });
- const d = await response.json();
- const a = d.address || d;
- return {
- street: a.Address || a.street || '123 Main St',
- city: a.City || a.city || 'New York',
- state: a.State_Full || a.State || a.state || 'New York',
- zip: String(a.Zip_Code || a.zip || '10001').substring(0, 5),
- };
- } catch (e) {
- console.error('[CheckoutAPI] 获取随机地址失败:', e.message);
- return { street: '123 Main St', city: 'New York', state: 'New York', zip: '10001' };
- }
- }
- /**
- * Generate a ChatGPT Plus Stripe hosted checkout URL.
- * @param {string} accessToken - ChatGPT session access token
- * @returns {Promise<{hostedUrl: string, checkoutSessionId: string}|null>}
- */
- async function generateCheckoutLink(accessToken) {
- try {
- const payload = {
- plan_name: 'chatgptplusplan',
- billing_details: { country: 'US', currency: 'USD' },
- cancel_url: 'https://chatgpt.com/#pricing',
- promo_campaign: { promo_campaign_id: 'plus-1-month-free', is_coupon_from_query_param: false },
- checkout_ui_mode: 'hosted',
- };
- const response = await fetch(CHATGPT_CHECKOUT_URL, {
- method: 'POST',
- headers: {
- 'Authorization': `Bearer ${accessToken}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(payload),
- });
- const data = await response.json();
- if (!response.ok) {
- console.error('[CheckoutAPI] 请求失败 HTTP', response.status, data);
- return null;
- }
- const hostedUrl = data?.url || data?.stripe_hosted_url || data?.checkout_url;
- if (!hostedUrl) {
- console.error('[CheckoutAPI] 未找到长链接', data);
- return null;
- }
- return {
- hostedUrl,
- checkoutSessionId: data.checkout_session_id || '',
- };
- } catch (e) {
- console.error('[CheckoutAPI] 生成 Plus 链接异常:', e.message);
- return null;
- }
- }
- function collectSmsTextValues(value, results = []) {
- if (value === null || value === undefined) {
- return results;
- }
- if (typeof value === 'string' || typeof value === 'number') {
- results.push(String(value));
- return results;
- }
- if (Array.isArray(value)) {
- value.forEach((item) => collectSmsTextValues(item, results));
- return results;
- }
- if (typeof value === 'object') {
- const preferredKeys = [
- 'code',
- 'sms',
- 'message',
- 'msg',
- 'content',
- 'text',
- 'data',
- 'result',
- ];
- preferredKeys.forEach((key) => {
- if (Object.prototype.hasOwnProperty.call(value, key)) {
- collectSmsTextValues(value[key], results);
- }
- });
- Object.keys(value)
- .filter((key) => !preferredKeys.includes(key))
- .forEach((key) => collectSmsTextValues(value[key], results));
- }
- return results;
- }
- function extractPaypalSmsCode(rawPayload, options = {}) {
- const excluded = new Set((options.excludeCodes || []).map((code) => String(code || '').trim()).filter(Boolean));
- const rawText = String(rawPayload || '').trim();
- if (!rawText) return '';
- let values = [rawText];
- try {
- values = collectSmsTextValues(JSON.parse(rawText));
- } catch {
- // Plain text API responses are expected.
- }
- const candidates = [];
- values.forEach((value) => {
- String(value || '').replace(/\b(\d{4,8})\b/g, (_, code) => {
- if (!excluded.has(code)) {
- candidates.push(code);
- }
- return _;
- });
- });
- return candidates.find((code) => code.length === 6)
- || candidates.find((code) => code.length >= 4 && code.length <= 8)
- || '';
- }
- async function fetchPaypalSmsCode(options = {}, deps = {}) {
- const fetchImpl = deps.fetchImpl || fetch;
- const sleep = deps.sleep || ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
- const addLog = deps.addLog || (async () => {});
- const throwIfStopped = deps.throwIfStopped || (() => {});
- const smsApiUrl = String(options.smsApiUrl || '').trim();
- if (!smsApiUrl) {
- throw new Error('PayPal 短信收码 API 未配置。');
- }
- const maxAttempts = Math.max(1, Math.floor(Number(options.maxAttempts) || DEFAULT_PAYPAL_SMS_MAX_ATTEMPTS));
- const intervalMs = Math.max(500, Math.floor(Number(options.intervalMs) || DEFAULT_PAYPAL_SMS_INTERVAL_MS));
- let lastError = '';
- for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
- throwIfStopped();
- try {
- const response = await fetchImpl(smsApiUrl, {
- method: 'GET',
- cache: 'no-store',
- });
- const text = await response.text();
- if (!response.ok) {
- lastError = `HTTP ${response.status}: ${text.slice(0, 120)}`;
- } else {
- const code = extractPaypalSmsCode(text, {
- excludeCodes: options.excludeCodes || [],
- });
- if (code) {
- return {
- code,
- attempt,
- raw: text,
- };
- }
- lastError = text.slice(0, 160) || '接口未返回验证码';
- }
- } catch (error) {
- lastError = error?.message || String(error || '请求失败');
- }
- if (attempt < maxAttempts) {
- await addLog(`PayPal 短信验证码暂未获取到,${Math.round(intervalMs / 1000)} 秒后重试(${attempt}/${maxAttempts})。`, 'info');
- await sleep(intervalMs);
- }
- }
- throw new Error(`PayPal 短信验证码获取超时。最后响应:${lastError || '无响应'}`);
- }
- return {
- extractPaypalSmsCode,
- fetchRandomAddress,
- fetchPaypalSmsCode,
- generateCheckoutLink,
- };
- })();
- })(typeof self !== 'undefined' ? self : globalThis);
|