150 lines
4.5 KiB
Go
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
|
|
}
|