package payment import ( "crypto/md5" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "net/url" "sort" "strings" "time" ) type BEpusdtConfig struct { ApiURL string ApiToken string TradeType string Fiat string Timeout int Rate string } type BEpusdtClient struct { config BEpusdtConfig client *http.Client } type CreateTransactionRequest struct { OrderID string `json:"order_id"` Amount float64 `json:"amount"` NotifyURL string `json:"notify_url"` RedirectURL string `json:"redirect_url"` Signature string `json:"signature"` TradeType string `json:"trade_type,omitempty"` Fiat string `json:"fiat,omitempty"` Name string `json:"name,omitempty"` Timeout int `json:"timeout,omitempty"` Rate string `json:"rate,omitempty"` Address string `json:"address,omitempty"` } type CreateTransactionResponse struct { StatusCode int `json:"status_code"` Message string `json:"message"` Data struct { Fiat string `json:"fiat"` TradeID string `json:"trade_id"` OrderID string `json:"order_id"` Amount interface{} `json:"amount"` ActualAmount interface{} `json:"actual_amount"` Status int `json:"status"` Token string `json:"token"` ExpirationTime int `json:"expiration_time"` PaymentURL string `json:"payment_url"` } `json:"data"` RequestID string `json:"request_id"` } type CallbackData struct { TradeID string `json:"trade_id"` OrderID string `json:"order_id"` Amount float64 `json:"amount"` ActualAmount float64 `json:"actual_amount"` Token string `json:"token"` BlockTransactionID string `json:"block_transaction_id"` Signature string `json:"signature"` Status int `json:"status"` } func NewBEpusdtClient(config BEpusdtConfig) *BEpusdtClient { if config.Fiat == "" { config.Fiat = "CNY" } if config.Timeout == 0 { config.Timeout = 600 } return &BEpusdtClient{ config: config, client: &http.Client{ Timeout: 30 * time.Second, }, } } func (c *BEpusdtClient) GenerateSignature(params map[string]string) string { keys := make([]string, 0, len(params)) for k := range params { if params[k] != "" && k != "signature" && k != "sign_type" { keys = append(keys, k) } } sort.Strings(keys) var parts []string for _, k := range keys { parts = append(parts, fmt.Sprintf("%s=%s", k, params[k])) } signStr := strings.Join(parts, "&") + c.config.ApiToken hash := md5.New() hash.Write([]byte(signStr)) return hex.EncodeToString(hash.Sum(nil)) } func (c *BEpusdtClient) CreateTransaction(orderID string, amount float64, notifyURL, redirectURL, name string) (*CreateTransactionResponse, error) { params := map[string]string{ "order_id": orderID, "amount": fmt.Sprintf("%.2f", amount), "notify_url": notifyURL, "redirect_url": redirectURL, } if c.config.TradeType != "" { params["trade_type"] = c.config.TradeType } if c.config.Fiat != "" { params["fiat"] = c.config.Fiat } if name != "" { params["name"] = name } if c.config.Timeout > 0 { params["timeout"] = fmt.Sprintf("%d", c.config.Timeout) } if c.config.Rate != "" { params["rate"] = c.config.Rate } params["signature"] = c.GenerateSignature(params) jsonData, err := json.Marshal(params) if err != nil { return nil, fmt.Errorf("failed to marshal request: %w", err) } apiURL := strings.TrimRight(c.config.ApiURL, "/") reqURL := apiURL + "/api/v1/order/create-transaction" req, err := http.NewRequest("POST", reqURL, strings.NewReader(string(jsonData))) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } req.Header.Set("Content-Type", "application/json") resp, err := c.client.Do(req) if err != nil { return nil, fmt.Errorf("failed to send request: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("failed to read response: %w", err) } var result CreateTransactionResponse if err := json.Unmarshal(body, &result); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } if result.StatusCode != 200 { return nil, fmt.Errorf("API error: %s (code: %d)", result.Message, result.StatusCode) } return &result, nil } func (c *BEpusdtClient) VerifyCallback(data CallbackData) bool { params := map[string]string{ "trade_id": data.TradeID, "order_id": data.OrderID, "amount": fmt.Sprintf("%.2f", data.Amount), "actual_amount": fmt.Sprintf("%.2f", data.ActualAmount), "token": data.Token, "block_transaction_id": data.BlockTransactionID, "status": fmt.Sprintf("%d", data.Status), } expectedSign := c.GenerateSignature(params) return strings.EqualFold(expectedSign, data.Signature) } func ParseCallbackFromQuery(query url.Values) CallbackData { var data CallbackData data.TradeID = query.Get("trade_id") data.OrderID = query.Get("order_id") data.Token = query.Get("token") data.BlockTransactionID = query.Get("block_transaction_id") data.Signature = query.Get("signature") if amount := query.Get("amount"); amount != "" { fmt.Sscanf(amount, "%f", &data.Amount) } if actualAmount := query.Get("actual_amount"); actualAmount != "" { fmt.Sscanf(actualAmount, "%f", &data.ActualAmount) } if status := query.Get("status"); status != "" { fmt.Sscanf(status, "%d", &data.Status) } return data } func ParseCallbackFromJSON(body []byte) (CallbackData, error) { var data CallbackData err := json.Unmarshal(body, &data) return data, err }