All checks were successful
Build docker and publish / build (20.15.1) (push) Successful in 6m26s
添加对已存在订阅的检查逻辑,避免重复处理相同的苹果IAP交易 添加测试恢复接口的示例代码
112 lines
3.8 KiB
Go
112 lines
3.8 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
pkgaes "github.com/perfect-panel/server/pkg/aes"
|
||
)
|
||
|
||
// 替换为您实际的服务器地址
|
||
const BaseURL = "https://api.hifast.biz"
|
||
|
||
// 替换为您实际的用户登录 Token (Authorization: Bearer <token>)
|
||
const UserToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJEZXZpY2VJZCI6MzgzLCJMb2dpblR5cGUiOiJkZXZpY2UiLCJTZXNzaW9uSWQiOiIwMTliMmFmZC1jMjUwLTc1YmItODQzMy04NDMyNWVmZGRkMzMiLCJVc2VySWQiOjM4MywiZXhwIjoxNzY2NTU3NjMyLCJpYXQiOjE3NjU5NTI4MzJ9.kkcT4ojXG9qn_aVqMaGqUUXhHcZXHy49k5Vn05Et9OM"
|
||
|
||
// 替换为您在后台配置的设备通信密钥 (Security Secret)
|
||
const DeviceSecret = "c0qhq99a-nq8h-ropg-wrlc-ezj4dlkxqpzx"
|
||
|
||
// 替换为您要测试的 Transaction ID
|
||
const TestTransactionID = "2000001083238483"
|
||
|
||
func main() {
|
||
fmt.Println("开始测试 Restore 接口 (AES 加密模式)...")
|
||
fmt.Printf("目标 Transaction ID: %s\n", TestTransactionID)
|
||
|
||
// 1. 构造原始请求数据
|
||
payload := map[string]interface{}{
|
||
"transactions": []string{TestTransactionID},
|
||
}
|
||
payloadBytes, _ := json.Marshal(payload)
|
||
fmt.Printf("原始请求体: %s\n", string(payloadBytes))
|
||
|
||
// 2. 加密数据
|
||
if DeviceSecret == "YOUR_DEVICE_SECRET_HERE" {
|
||
log.Fatal("❌ 请在代码中设置 DeviceSecret (对应后台配置的 Security Secret)")
|
||
}
|
||
encryptedData, iv, err := pkgaes.Encrypt(payloadBytes, DeviceSecret)
|
||
if err != nil {
|
||
log.Fatalf("加密失败: %v", err)
|
||
}
|
||
|
||
// 3. 构造最终的请求体 (符合 DeviceMiddleware 要求的格式)
|
||
// DeviceMiddleware 期望的格式是: { "data": "Base64Cipher", "time": "Nonce/IV" }
|
||
// 或者直接在 URL Query 中传 ?data=...&time=...
|
||
// 这里我们模拟 POST JSON body 的方式
|
||
finalPayload := map[string]interface{}{
|
||
"data": encryptedData,
|
||
"time": iv,
|
||
}
|
||
finalBytes, _ := json.Marshal(finalPayload)
|
||
fmt.Printf("加密后请求体: %s\n", string(finalBytes))
|
||
|
||
url := fmt.Sprintf("%s/v1/public/iap/apple/restore", BaseURL)
|
||
req, _ := http.NewRequest("POST", url, strings.NewReader(string(finalBytes)))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
|
||
// 添加必要的 Header 以通过 DeviceMiddleware
|
||
req.Header.Set("Login-Type", "device") // 触发 DeviceMiddleware 的解密逻辑
|
||
// 注意:这里需要替换为真实有效的 Bearer Token,否则会报 401
|
||
// 您可以先登录后台或者使用 cmd/test_apple_iap 工具生成的 token 也是不可用的,必须是业务系统的 token
|
||
// 为了演示,这里留空,实际运行前请填入
|
||
if UserToken != "YOUR_USER_TOKEN_HERE" {
|
||
req.Header.Set("Authorization", "Bearer "+UserToken)
|
||
} else {
|
||
fmt.Println("⚠️ 警告: 未设置 UserToken,请求可能会失败 (401 Unauthorized)")
|
||
}
|
||
|
||
client := &http.Client{Timeout: 10 * time.Second}
|
||
start := time.Now()
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
log.Fatalf("请求失败: %v", err)
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
duration := time.Since(start)
|
||
body, _ := io.ReadAll(resp.Body)
|
||
|
||
fmt.Printf("请求耗时: %v\n", duration)
|
||
fmt.Printf("状态码: %d\n", resp.StatusCode)
|
||
fmt.Printf("响应内容: %s\n", string(body))
|
||
|
||
if resp.StatusCode == 200 {
|
||
var result map[string]interface{}
|
||
if err := json.Unmarshal(body, &result); err == nil {
|
||
// 检查业务状态码
|
||
if code, ok := result["code"].(float64); ok && int(code) != 200 {
|
||
fmt.Printf("❌ 业务处理失败: code=%d, msg=%s\n", int(code), result["msg"])
|
||
return
|
||
}
|
||
|
||
fmt.Println("✅ Restore 接口调用成功!")
|
||
if data, ok := result["data"].(map[string]interface{}); ok {
|
||
if success, ok := data["success"].(bool); ok && success {
|
||
fmt.Println(" 业务处理成功: success=true")
|
||
} else {
|
||
fmt.Println(" 业务处理结果未知:", data)
|
||
}
|
||
} else {
|
||
fmt.Println(" 无数据返回或格式不符")
|
||
}
|
||
}
|
||
} else {
|
||
fmt.Println("❌ 接口调用失败")
|
||
}
|
||
}
|