Files
gpt-plus-gpt/pkg/stripe/aimizy.go
2026-03-15 20:48:19 +08:00

150 lines
4.5 KiB
Go

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
}