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) { }