1
This commit is contained in:
295
pkg/udunhelper/udun.go
Normal file
295
pkg/udunhelper/udun.go
Normal file
@ -0,0 +1,295 @@
|
||||
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) {
|
||||
|
||||
}
|
||||
153
pkg/udunhelper/udunmodel.go
Normal file
153
pkg/udunhelper/udunmodel.go
Normal file
@ -0,0 +1,153 @@
|
||||
package udunhelper
|
||||
|
||||
type VerifyRequest struct {
|
||||
MerchantId string `json:"merchantId"`
|
||||
MainCoinType string `json:"mainCoinType"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type CallbackRequest struct {
|
||||
MerchantId string `json:"merchantId"` //商户号
|
||||
MainCoinType int `json:"mainCoinType"` //主幣種編號,使用獲取商戶幣種信息接口
|
||||
CallUrl string `json:"callUrl"` //回調地址,通過該接口創建的地址,以後關於該地址的充幣信息會通過您指定的回調地址通知您
|
||||
WalletId string `json:"walletId"` //錢包編號,默認根據主錢包生成地址,可不填写
|
||||
Alias string `json:"alias"` //地址別名,可不填写
|
||||
}
|
||||
|
||||
type WithdrawRequest struct {
|
||||
/**
|
||||
{
|
||||
"address":"raadSxrUhG5EQVCY75CSGaVLWCeXd6yH6s",
|
||||
"amount":"0.11",
|
||||
"merchantId":"100109",
|
||||
"mainCoinType":"144",
|
||||
"coinType":"144",
|
||||
"callUrl":"http://localhost:8080/mch/callBack",
|
||||
"businessId":"15",
|
||||
"memo":"10112"
|
||||
}
|
||||
*/
|
||||
|
||||
Address string `json:"address"` //提币地址
|
||||
Amount string `json:"amount"` //提币数量
|
||||
MerchantId string `json:"merchantId"` //商户号
|
||||
MainCoinType string `json:"mainCoinType"` //主幣種編號,使用獲取商戶幣種信息接口
|
||||
CoinType string `json:"coinType"` //子幣種編號,使用獲取商戶幣種信息接口
|
||||
CallUrl string `json:"callUrl"` //回調地址,通過該callUrl告知您該筆提幣交易的狀態
|
||||
BusinessId string `json:"businessId"` //業務編號,必須保證該字段在系統內唯一,如果重復,則該筆提幣錢包將不會進行接收
|
||||
WalletId string `json:"-"`
|
||||
Memo string `json:"memo"` //備註,XRP和EOS,這兩種幣的提幣申請該字段可選,其他類型幣種不填
|
||||
}
|
||||
type BaseRequest struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Nonce int `json:"nonce"`
|
||||
}
|
||||
|
||||
type HttpRequest struct {
|
||||
Timestamp int64 `json:"timestamp"` //时间戳
|
||||
Nonce int `json:"nonce"` //随机数
|
||||
Sign string `json:"sign"` //签名
|
||||
Body string `json:"body"` //请求body
|
||||
}
|
||||
|
||||
type BaseRes struct {
|
||||
Code int `json:"code"` //200才是返回成功调用
|
||||
Message string `json:"message"` //
|
||||
}
|
||||
|
||||
type BaseCoinsMerchant struct {
|
||||
Code int `json:"code"` //200才是返回成功调用
|
||||
Message string `json:"message"` //
|
||||
Data []CoinsMerchant `json:"data"` //
|
||||
}
|
||||
|
||||
//CoinsMerchant 商家持仓的币种信息
|
||||
type CoinsMerchant struct {
|
||||
MainCoinType string `json:"mainCoinType"` //主幣種類型
|
||||
CoinType string `json:"coinType"` // 幣種類型
|
||||
Symbol string `json:"symbol"` //BTC
|
||||
Name string `json:"name"` //幣種別名,BTC
|
||||
Logo string `json:"logo"` //幣種log地址
|
||||
CoinName string `json:"coinName"` //幣種全稱,Bitcoin
|
||||
MainSymbol string `json:"mainSymbol"` //主幣種單位
|
||||
Decimals string `json:"decimals"` //幣種精度,8
|
||||
TokenStatus int `json:"tokenStatus"` //0: 主幣 1:代幣
|
||||
Balance int64 `json:"balance"` //幣種余額
|
||||
}
|
||||
|
||||
type BaseCoinAddress struct {
|
||||
Code int `json:"code"` //200才是返回成功调用
|
||||
Message string `json:"message"` //
|
||||
Data CoinAddress `json:"data"` //
|
||||
}
|
||||
|
||||
//CoinAddress 地址返回
|
||||
type CoinAddress struct {
|
||||
CoinType int `json:"coinType"` // 幣種類型
|
||||
Address string `json:"address"` //地址
|
||||
}
|
||||
|
||||
type CallBackBase struct {
|
||||
TimeStamp int64 `json:"timestamp"`
|
||||
Nonce int `json:"nonce"`
|
||||
Sign string `json:"sign"`
|
||||
Body CallBackRes `json:"body"` //
|
||||
}
|
||||
|
||||
//CallBackRes 回调结构体
|
||||
type CallBackRes struct {
|
||||
Address string `json:"address"` //地址
|
||||
Amount string `json:"amount"` //交易數量,根據幣種精度獲取實際金額,實際金額=amount/pow(10,decimals),即實際金額等於amount除以10的decimals次方
|
||||
Fee string `json:"fee"` //礦工費,根據幣種精度獲取實際金額,實際金額獲取同上
|
||||
Decimals string `json:"decimals"` //幣種精度
|
||||
CoinType string `json:"coinType"` //子幣種編號,使用獲取商戶幣種信息接口
|
||||
MainCoinType string `json:"mainCoinType"` //主幣種類型
|
||||
BusinessId string `json:"businessId"` //業務編號,提幣回調時為提幣請求時傳入的,充幣回調無值
|
||||
BlockHigh string `json:"blockHigh"` //區塊高度
|
||||
TradeId string `json:"tradeId"` //業務流水號
|
||||
TxId string `json:"txid"` //區塊鏈交易哈希
|
||||
Memo string `json:"memo"` //備註,XRP和EOS,使用獲取商戶幣種信息接口,這2種類型幣的充提幣可能有值
|
||||
Status int `json:"status"` //狀態,0待審核,1審核成功,2審核駁回,3交易成功,4交易失敗
|
||||
TradeType int `json:"tradeType"` //交易類型,1充幣回調,2提幣回調
|
||||
}
|
||||
|
||||
func getWithDrawMsg(code int) string {
|
||||
msg := ""
|
||||
switch code {
|
||||
case 200:
|
||||
msg = "提币成功"
|
||||
case 523:
|
||||
msg = "参数为空"
|
||||
case 581:
|
||||
msg = "无效的提币金额"
|
||||
case 4005:
|
||||
msg = "非法参数"
|
||||
case 4034:
|
||||
msg = "未找到该币种信息"
|
||||
case 4162:
|
||||
msg = "签名异常"
|
||||
case 4163:
|
||||
msg = "签名错误"
|
||||
case 4169:
|
||||
msg = "商户已被禁用"
|
||||
case 4183:
|
||||
msg = "到账地址异常"
|
||||
case 4193:
|
||||
msg = "EOS金额小数点后超过4位长度"
|
||||
case 4214:
|
||||
msg = "暂无可用的币种"
|
||||
case 4226:
|
||||
msg = "商户普通账户被禁用"
|
||||
case 4261:
|
||||
msg = "商户管理员账户被禁用"
|
||||
case 4284:
|
||||
msg = "商户不存在"
|
||||
case 4288:
|
||||
msg = "业务编号重复,请勿重复申请"
|
||||
case 4598:
|
||||
msg = "传入body中的list对象中的所有merchantId必须保持一致"
|
||||
case 4001:
|
||||
msg = "商户不存在"
|
||||
}
|
||||
return msg
|
||||
}
|
||||
Reference in New Issue
Block a user