package main import ( "crypto/ecdsa" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "fmt" "io" "log" "net/http" "time" ) // 配置区域 - 请在此处填入您的真实信息进行测试 const ( // 必填:您的 Key ID (从 App Store Connect 获取) KeyID = "2C4X3HVPM8" // 必填:您的 Issuer ID (从 App Store Connect 获取,通常是一个 UUID) IssuerID = "34f54810-5118-4b7f-8069-c8c1e012b7a9" // 请替换为您真实的 Issuer ID // 必填:您的 Bundle ID (App 的包名) BundleID = "com.taw.hifastvpn" // 请替换为您真实的 Bundle ID // 必填:用于测试的 Transaction ID (任意一个真实的交易 ID) TestTransactionID = "2000001083318819" // 必填:是否为沙盒环境 IsSandbox = true ) // P8 私钥内容 (硬编码用于测试) const PrivateKeyPEM = `-----BEGIN PRIVATE KEY----- MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgsVDj0g/D7uNCm8aC E4TuaiDT4Pgb1IuuZ69YdGNvcAegCgYIKoZIzj0DAQehRANCAARObgGumaESbPMM SIRDAVLcWemp0fMlnfDE4EHmqcD58arEJWsr3aWEhc4BHocOUIGjko0cVWGchrFa /T/KG1tr -----END PRIVATE KEY-----` func main() { log.Println("开始测试 Apple IAP API 连接...") log.Printf("环境: %v (Sandbox=%v)\n", func() string { if IsSandbox { return "沙盒 (Sandbox)" } return "生产 (Production)" }(), IsSandbox) log.Printf("KeyID: %s\n", KeyID) log.Printf("IssuerID: %s\n", IssuerID) log.Printf("BundleID: %s\n", BundleID) log.Printf("TransactionID: %s\n", TestTransactionID) token, err := buildAPIToken() if err != nil { log.Fatalf("生成 JWT Token 失败: %v", err) } log.Println("JWT Token 生成成功") // 发起请求 host := "https://api.storekit.itunes.apple.com" if IsSandbox { host = "https://api.storekit-sandbox.itunes.apple.com" } url := fmt.Sprintf("%s/inApps/v1/transactions/%s", host, TestTransactionID) req, _ := http.NewRequest("GET", url, nil) req.Header.Set("Authorization", "Bearer "+token) log.Printf("正在请求: %s", url) start := time.Now() resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatalf("请求失败: %v", err) } defer resp.Body.Close() duration := time.Since(start) body, _ := io.ReadAll(resp.Body) log.Printf("请求耗时: %v", duration) log.Printf("状态码: %d", resp.StatusCode) if resp.StatusCode == 200 { log.Println("✅ 测试成功!API 调用正常。") log.Printf("响应内容: %s", string(body)) } else { log.Println("❌ 测试失败!") log.Printf("错误响应: %s", string(body)) if resp.StatusCode == 401 { log.Println("原因分析: 401 Unauthorized 通常表示:") log.Println("1. Key ID 或 Issuer ID 错误") log.Println("2. Bundle ID 不匹配") log.Println("3. 私钥错误") log.Println("4. Token 格式错误 (如算法或 Claims)") } else if resp.StatusCode == 404 { log.Println("原因分析: 404 Not Found 通常表示 Transaction ID 不存在或环境(沙盒/生产)选错了") } } } // 下面是复制过来的工具函数 func buildAPIToken() (string, error) { header := map[string]interface{}{ "alg": "ES256", "kid": KeyID, "typ": "JWT", } now := time.Now().Unix() payload := map[string]interface{}{ "iss": IssuerID, "iat": now, "exp": now + 60, // 测试 Token 有效期短一点即可 "aud": "appstoreconnect-v1", } if BundleID != "" { payload["bid"] = BundleID } hb, _ := json.Marshal(header) pb, _ := json.Marshal(payload) enc := func(b []byte) string { return base64.RawURLEncoding.EncodeToString(b) } unsigned := fmt.Sprintf("%s.%s", enc(hb), enc(pb)) block, _ := pem.Decode([]byte(PrivateKeyPEM)) if block == nil { return "", fmt.Errorf("invalid private key") } keyAny, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return "", err } priv, ok := keyAny.(*ecdsa.PrivateKey) if !ok { return "", fmt.Errorf("private key is not ECDSA") } digest := sha256Sum([]byte(unsigned)) r, s, err := ecdsa.Sign(rand.Reader, priv, digest) if err != nil { return "", err } curveBits := priv.Curve.Params().BitSize keyBytes := curveBits / 8 if curveBits%8 > 0 { keyBytes += 1 } rBytes := r.Bytes() rBytesPadded := make([]byte, keyBytes) copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) sBytes := s.Bytes() sBytesPadded := make([]byte, keyBytes) copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) sig := append(rBytesPadded, sBytesPadded...) return unsigned + "." + base64.RawURLEncoding.EncodeToString(sig), nil } func sha256Sum(b []byte) []byte { h := sha256.New() h.Write(b) return h.Sum(nil) }