package stripe import ( "encoding/json" "fmt" "io" "log" "net/http" "regexp" "strings" "time" "gpt-plus/config" ) // aimizyResponse represents the JSON response from the Aimizy API. type aimizyResponse struct { Success bool `json:"success"` CheckoutSessionID string `json:"checkout_session_id"` URL string `json:"url"` PublishableKey string `json:"publishable_key"` Error string `json:"error"` Message string `json:"message"` } var csLiveRe = regexp.MustCompile(`cs_live_[A-Za-z0-9]+`) // CreateCheckoutViaAimizy uses the Aimizy API to generate a $0 checkout session. // This is an alternative to calling ChatGPT's /backend-api/payments/checkout directly. // Returns a CheckoutResult compatible with the normal Stripe flow. func CreateCheckoutViaAimizy(aimizy config.AimizyConfig, accessToken, country, currency, planName, promoCampaignID string, seatQuantity int) (*CheckoutResult, error) { payload := map[string]interface{}{ "access_token": accessToken, "check_card_proxy": false, "country": country, "currency": currency, "is_coupon_from_query_param": true, "is_short_link": true, "plan_name": planName, "price_interval": "month", "promo_campaign_id": promoCampaignID, } // Team plan needs seat_quantity if seatQuantity > 0 { payload["seat_quantity"] = seatQuantity } jsonBody, err := json.Marshal(payload) if err != nil { return nil, fmt.Errorf("marshal aimizy payload: %w", err) } apiURL := aimizy.BaseURL + "/api/public/generate-payment-link" var resp *http.Response var respBody []byte directClient := &http.Client{Timeout: 30 * time.Second} for try := 1; try <= 3; try++ { req, err := http.NewRequest("POST", apiURL, strings.NewReader(string(jsonBody))) if err != nil { return nil, fmt.Errorf("build aimizy request: %w", err) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") req.Header.Set("Origin", aimizy.BaseURL) req.Header.Set("Referer", aimizy.BaseURL+"/pay") req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36") resp, err = directClient.Do(req) if err != nil { log.Printf("[aimizy] attempt %d/3: request error: %v", try, err) if try < 3 { time.Sleep(3 * time.Second) continue } return nil, fmt.Errorf("aimizy request failed after 3 attempts: %w", err) } respBody, err = io.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, fmt.Errorf("read aimizy response: %w", err) } // Log full response headers and body log.Printf("[aimizy] attempt %d/3: status=%d", try, resp.StatusCode) for k, vs := range resp.Header { for _, v := range vs { log.Printf("[aimizy] response header: %s: %s", k, v) } } log.Printf("[aimizy] response body: %s", string(respBody)) if resp.StatusCode == 200 { break } log.Printf("[aimizy] attempt %d/3: status %d, retrying...", try, resp.StatusCode) if try < 3 { time.Sleep(3 * time.Second) } } if resp == nil || resp.StatusCode != 200 { statusCode := 0 if resp != nil { statusCode = resp.StatusCode } return nil, fmt.Errorf("aimizy failed: status %d, body: %s", statusCode, string(respBody)) } var result aimizyResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, fmt.Errorf("parse aimizy response: %w (body: %s)", err, string(respBody)) } if !result.Success { return nil, fmt.Errorf("aimizy returned success=false: %s", string(respBody)) } // Extract checkout_session_id csID := result.CheckoutSessionID if csID == "" && result.URL != "" { // Fallback: extract from URL if m := csLiveRe.FindString(result.URL); m != "" { csID = m } } if csID == "" { return nil, fmt.Errorf("aimizy returned no checkout_session_id: %s", string(respBody)) } // Build compatible CheckoutResult cr := &CheckoutResult{ CheckoutSessionID: csID, ProcessorEntity: "openai_llc", PublishableKey: result.PublishableKey, } // If publishable_key not in aimizy response, use the known production key if cr.PublishableKey == "" { cr.PublishableKey = "pk_live_51HOrSwC6h1nxGoI3lTAgRjYVrz4dU3fVOabyCcKR3pbEJguCVAlqCxdxCUvoRh1XWwRacViovU3kLKvpkjh7IqkW00iXQsjo3n" } log.Printf("[aimizy] checkout session created: %s (key=%s)", cr.CheckoutSessionID, cr.PublishableKey[:20]+"...") return cr, nil }