Files
2025-02-06 11:14:33 +08:00

296 lines
9.2 KiB
Go

package udunhelper
import (
"crypto/md5"
"crypto/tls"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
log "github.com/go-admin-team/go-admin-core/logger"
ext "go-admin/config"
"go-admin/pkg/jsonhelper"
"go-admin/pkg/utility/seqs"
"io"
"net/http"
"strings"
"time"
"go.uber.org/zap"
)
const (
createAddressUrl = "/mch/address/create"
withdrawUrl = "/mch/withdraw"
checkAddressUrl = "/mch/check/address"
supportCoinsUrl = "/mch/support-coins"
existAddressUrl = "/mch/exist/address"
Notify = "/api/v1/line/notify"
)
var (
clientUDun *http.Client
)
func init() {
t := http.DefaultTransport.(*http.Transport).Clone()
t.MaxIdleConns = 100
t.MaxConnsPerHost = 10000
t.MaxIdleConnsPerHost = 100
t.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
//u盾client
clientUDun = &http.Client{
Timeout: 50 * time.Second, //time.Duration(timeout) * time.Second,
Transport: t,
}
//UdKey = config.AppGlobalConfig.UDunKey // "d333ae13beb3a96c0225847098267cf3"
//UdMerchantId = config.AppGlobalConfig.UDunMerchantID // "311129"
//CallUrl = config.AppGlobalConfig.CurrServerIp + "/api/recharge/notify" // "https://8.218.110.85/api/recharge/notify" // 回调地址
//baseUrl = config.AppGlobalConfig.UDunUrl //"https://sig10.udun.io"
}
/* 回调例子
"timestamp": 1535005047,
"nonce": 100000,
"sign": "e1bee3a417b9c606ba6cedda26db761a",
"body": "{\"address\":\"DJY781Z8qbuJeuA7C3McYivbX8kmAUXPsW\",\"amount\":\"12345678\",\"blockHigh\":\"102419\",\"coinType\":\"206\",\"decimals\":\"8\",\"fee\":\"452000\",
\"mainCoinType\":\"206\",\"status\":3,\"tradeId\":\"20181024175416907\",\"tradeType\":1,\"txId\":\"31689c332536b56a2246347e206fbed2d04d461a3d668c4c1de32a75a8d436f0\"}"
*/
// GetSupportCoinsByMerchant 获取商家支持币种
func GetSupportCoinsByMerchant(baseReq BaseRequest, showBalance bool) BaseCoinsMerchant {
mp := make(map[string]interface{}, 0)
//mp["merchantId"] = config.AppGlobalConfig.UDunMerchantID
mp["merchantId"] = ext.ExtConfig.UDunConfig.UDunMerchantID
mp["showBalance"] = showBalance
reqBody, _ := json.Marshal(mp)
sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, supportCoinsUrl)
re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
if err != nil {
log.Error("GetSupportCoinsByMerchant", zap.Error(err))
return BaseCoinsMerchant{}
}
var res BaseCoinsMerchant
err = jsonhelper.Unmarshal(re, &res)
if err != nil {
return BaseCoinsMerchant{}
}
return res
}
// CreateAddress 创建地址
func CreateAddress(baseReq BaseRequest, callback []*CallbackRequest) BaseCoinAddress {
for _, c := range callback {
c.MerchantId = ext.ExtConfig.UDunConfig.UDunMerchantID
c.CallUrl = ext.ExtConfig.UDunConfig.CurrServerIp + Notify // "/api/recharge/notify"
}
reqBody, _ := json.Marshal(callback)
sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, createAddressUrl)
re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
if err != nil {
log.Error("CreateAddress", zap.Error(err), zap.String("url", url))
return BaseCoinAddress{}
}
var res BaseCoinAddress
err = jsonhelper.Unmarshal(re, &res)
if err != nil {
log.Error("CreateAddress Unmarshal", zap.Error(err))
return BaseCoinAddress{}
}
return res
}
// Withdraw 提现
func Withdraw(withdraw []*WithdrawRequest) (code int, msg string) {
timeStamp := time.Now().Unix()
nonce := seqs.Rand().RandInt(600000)
// 序列化
reqBody, _ := json.Marshal(withdraw)
// 签名
sign := Signature(nonce, timeStamp, string(reqBody))
url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, withdrawUrl)
// 提交
re, err := sendRequestUDun(url, nonce, timeStamp, string(reqBody), sign)
if err != nil {
log.Error("Withdraw", zap.Error(err))
return 0, "调用失败"
}
// 反序列化
var res BaseRes
if err := jsonhelper.Unmarshal(re, &res); err != nil {
return 0, "Withdraw jsonhelper.Unmarshal失败"
}
return res.Code, getWithDrawMsg(res.Code) //res.Message
}
// CheckAddress 检查地址是否正确
func CheckAddress(baseReq BaseRequest, verify []*VerifyRequest) (code int, msg string) {
reqBody, _ := json.Marshal(verify)
sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, checkAddressUrl)
re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
if err != nil {
log.Error("CheckAddress", zap.Error(err))
return 0, "调用失败"
}
var res BaseRes
err = jsonhelper.Unmarshal(re, &res)
if err != nil {
return 0, "jsonhelper.Unmarshal失败"
}
return res.Code, res.Message
}
// ExistAddress 检查地址是否在udun存在
func ExistAddress(baseReq BaseRequest, verify []*VerifyRequest) (code int, msg string) {
reqBody, _ := json.Marshal(verify)
sign := Signature(baseReq.Nonce, baseReq.Timestamp, string(reqBody))
url := fmt.Sprintf("%v%v", ext.ExtConfig.UDunConfig.UDunUrl, existAddressUrl)
re, err := sendRequestUDun(url, baseReq.Nonce, baseReq.Timestamp, string(reqBody), sign)
if err != nil {
log.Error("ExistAddress", zap.Error(err))
return 0, "调用失败"
}
var res BaseRes
err = jsonhelper.Unmarshal(re, &res)
if err != nil {
return 0, "jsonhelper.Unmarshal失败"
}
return res.Code, res.Message
}
func Signature(nonce int, timestamp int64, body string) string {
msg := fmt.Sprintf("%v%v%v%v", body, ext.ExtConfig.UDunConfig.UDunKey, nonce, timestamp)
h := md5.New()
h.Write([]byte(msg))
return hex.EncodeToString(h.Sum(nil))
}
func CheckCallBackSign(timestamp string, nonce string, body string) string {
raw := body + ext.ExtConfig.UDunConfig.UDunKey + nonce + timestamp
h := md5.New()
h.Write([]byte(raw))
return hex.EncodeToString(h.Sum(nil))
}
func sendRequestUDun(url string, nonce int, timestamp int64, reqBody string, sign string) ([]byte, error) {
q := HttpRequest{Nonce: nonce, Timestamp: timestamp, Sign: sign, Body: reqBody}
body, _ := json.Marshal(q)
headers := map[string]string{}
headers["Content-Type"] = "application/json"
resp, err := newHttpRequest(url, string(body), headers)
if err != nil {
return nil, err
}
return resp, nil
}
func newHttpRequest(reqUrl, postData string, reqHeaders map[string]string) ([]byte, error) {
req, _ := http.NewRequest("POST", reqUrl, strings.NewReader(postData))
if req.Header.Get("User-Agent") == "" {
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36")
}
if reqHeaders != nil {
for k, v := range reqHeaders {
req.Header.Add(k, v)
}
}
resp, err := clientUDun.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
bodyData, err := io.ReadAll(resp.Body) // ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
arr := make([]byte, len(bodyData))
copy(arr, bodyData)
if resp.StatusCode != 200 {
return nil, errors.New(string(arr))
}
return arr, nil
}
/*
createAddressUrl 创建地址 接口返回code说明
-1 生成地址失敗
200 生成地址成功
4001 商户不存在
4005 非法參數
4045 幣種信息錯誤
4162 簽名異常
4163 簽名錯誤
4166 商戶沒有配置套餐
4168 商戶地址達到上限
4169 商戶已禁用
4175 錢包編號錯誤
4017 商戶沒有創建錢包
4176 錢包未添加支持該幣種
4188 暫不支持
4226 商戶普通賬戶被禁用
4261 商戶管理員賬戶被禁用
4262 賬戶不存在
withdrawUrl 提币 接口返回code说明
200 提幣成功
523 參數為空
581 無效的提幣金額
4005 非法參數
4014 幣種為空
4034 未找到該幣種信息
4162 簽名異常
4163 簽名錯誤
4169 商戶已被禁用
4183 到賬地址異常
4193 EOS金額小數點後超過4位長度
4214 暫無可用的幣種
4226 商戶普通賬戶被禁用
4261 商戶管理員賬戶被禁用
4284 商户不存在
4288 業務編號(BusinessId)重復,請勿重復申請
4598 傳入body中的list對象中的所有merchantId必須保持一致
4001 商户不存在
*/
//分充值回調和提幣回調, 其中提幣最多會進行兩次回調( 審核回調 + 交易結果回調)
//func SendRequest(url string, nonce int, timestamp int64, reqBody string, sign string) (code int64, msg string, extra string) {
// q := HttpRequest{Nonce: nonce, Timestamp: timestamp, Sign: sign, Body: reqBody}
// body, _ := json.Marshal(q)
// resp, err := clientUDun.Post(url, "application/json", bytes.NewBuffer(body))
// if err != nil {
// return http.StatusBadRequest, err.Error(), ""
// }
// defer resp.Body.Close()
// r, err := ioutil.ReadAll(resp.Body)
// if err != nil {
// return http.StatusBadGateway, err.Error(), ""
// }
//
// js := gjson.ParseBytes(r)
// code = js.Get("code").Int()
// msg = js.Get("message").String()
// if js.Get("data").Exists() {
// extra = js.Get("data").String()
// }
// return
//}
// CallBackFun 網關收到交易處理結果,調用商戶提供的回調接口,通知商戶具體變化信息。該接口網關發送給您指定的回調地址的內容,處理您的業務信息。
// 分充值回調和提幣回調, 其中提幣最多會進行兩次回調( 審核回調 + 交易結果回調)
func CallBackFun(res CallBackBase) {
}