Initial sanitized code sync
This commit is contained in:
149
pkg/stripe/aimizy.go
Normal file
149
pkg/stripe/aimizy.go
Normal file
@@ -0,0 +1,149 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user