package services import ( "bytes" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "io" "math/rand" "net/http" "net/url" "strconv" "strings" "time" ) const ( BaseURL = "https://sandboxapi.rapyd.net" SecretKey = "rsk_a97addbe02a32a153eaedf1db35c11ce24e4b6c7257c01eb5953d59ad5d281d8f1b1c6a60b68b" AccessKey = "rak_EC66D246035CA48157FA" ) func generateSalt(length int) string { const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, length) for i := range b { b[i] = charset[rand.Intn(len(charset))] } return string(b) } func getUnixTime() int64 { return 1747194248 } func createSignature(method, urlPath string, body []byte, salt string, timestamp int64) string { bodyStr := string(body) toSign := strings.ToLower(method) + urlPath + salt + strconv.FormatInt(timestamp, 10) + AccessKey + bodyStr fmt.Println("Data to sign (Go):", toSign) h := hmac.New(sha256.New, []byte(SecretKey)) h.Write([]byte(toSign)) // Elimina padding '=' al final encoder := base64.URLEncoding.WithPadding(base64.NoPadding) signature := encoder.EncodeToString(h.Sum(nil)) fmt.Printf("HMAC (Go hex): %x\n", h.Sum(nil)) fmt.Println("Signature (Go):", signature) return signature } func createHeaders(method, fullURL string, body interface{}) ([]byte, map[string]string, error) { // Serializar cuerpo como JSON sin espacios var bodyBytes []byte var err error if body != nil { bodyBytes, err = json.Marshal(body) if err != nil { return nil, nil, err } } else { bodyBytes = []byte{} } // Obtener solo la ruta `/v1/...` desde fullURL u, err := url.Parse(fullURL) if err != nil { return nil, nil, err } urlPath := u.Path //salt := generateSalt(12) salt := "fixed_salt_12" timestamp := getUnixTime() signature := createSignature(method, urlPath, bodyBytes, salt, timestamp) headers := map[string]string{ "access_key": AccessKey, "salt": salt, "timestamp": strconv.FormatInt(timestamp, 10), "signature": signature, "idempotency": strconv.FormatInt(time.Now().UnixNano(), 10), "Content-Type": "application/json", } return bodyBytes, headers, nil } func MakeRequest(method, path string, body interface{}) (map[string]interface{}, error) { fullURL := BaseURL + path urlPath := path // Serializa el cuerpo sin espacios ni reordenamientos bodyBytes, err := json.Marshal(body) if err != nil { return nil, err } salt := "fixed_salt_12" timestamp := int64(1747194248) // Fijo para comparar con Python signature := createSignature(method, urlPath, bodyBytes, salt, timestamp) headers := map[string]string{ "access_key": AccessKey, "salt": salt, "timestamp": strconv.FormatInt(timestamp, 10), "signature": signature, "idempotency": strconv.FormatInt(time.Now().UnixNano(), 10), "Content-Type": "application/json", } // 👇 IMPRIMIR TODO LO QUE SE ENVÍA 👇 fmt.Println("➡️ REQUEST ENVIADO:") fmt.Println("Method:", method) fmt.Println("URL:", fullURL) fmt.Println("Headers:") for k, v := range headers { fmt.Printf(" %s: %s\n", k, v) } fmt.Println("Body:") fmt.Println(string(bodyBytes)) fmt.Println(strings.Repeat("-", 50)) // Crea request req, err := http.NewRequest(strings.ToUpper(method), fullURL, bytes.NewReader(bodyBytes)) if err != nil { return nil, err } for k, v := range headers { req.Header.Set(k, v) } // 👇 OPCIONAL: Imprimir curl equivalente 👇 curlCmd := fmt.Sprintf("curl -X %s '%s' \\\n", req.Method, req.URL.String()) for k, v := range req.Header { curlCmd += fmt.Sprintf(" -H '%s: %s' \\\n", k, v[0]) } if len(bodyBytes) > 0 { curlCmd += fmt.Sprintf(" -d '%s'", string(bodyBytes)) } fmt.Println("📋 Comando curl equivalente:") fmt.Println(curlCmd) fmt.Println(strings.Repeat("-", 50)) // Envía la solicitud client := &http.Client{Timeout: 30 * time.Second} resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode != 200 { return nil, fmt.Errorf("status %d: %s", resp.StatusCode, string(respBody)) } var result map[string]interface{} if err := json.Unmarshal(respBody, &result); err != nil { return nil, err } return result, nil }