Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user