package moneydealernative import ( "crypto/hmac" "crypto/md5" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "errors" "fmt" "io/ioutil" "net/http" "server/modules/pay/base" "server/modules/pay/values" "server/pb" "sort" "strings" "time" "github.com/google/uuid" jsoniter "github.com/json-iterator/go" "github.com/gogo/protobuf/proto" "github.com/liangdas/mqant/log" ) func NewSub(b *base.Base) { sub := &Sub{ Base: b, } b.HttpType = base.HttpTypeJson b.PostformType = base.PostformTypeQuery b.ShouldSignUpper = true if b.Opt == base.OPTPay { b.Resp = new(PayResp) b.ReqURL = getUrl() + payURL b.SubReq = sub.SubPayReq } else if b.Opt == base.OPTWithdraw { b.Resp = new(WithdrawResp) b.ReqURL = getUrl() + withdrawURL // todo b.SubReq = sub.SubPayReq } else if b.Opt == base.OPTPayCB { b.CallbackReq = new(PayCallbackReq) b.CallbackResp.Msg = "success" } else if b.Opt == base.OPTWithdrawCB { b.CallbackReq = new(WithdrawCallbackReq) b.CallbackResp.Msg = "success" } else if b.Opt == base.OPTQueryWithdraw { // 查询 b.HttpType = base.HttpTypeGet b.Resp = new(QueryWithdrawResp) b.ReqURL = getUrl() + queryWithdrawURL + fmt.Sprintf("?order_no=%s", b.QueryWithdrawReq.APIOrderID) b.SubReq = sub.SubPayReq } else if b.Opt == base.OPTQueryPay { // 查询 b.HttpType = base.HttpTypeGet b.Resp = new(QueryPayResp) b.ReqURL = getUrl() + queryPayURL + fmt.Sprintf("?order_no=%s", b.QueryPayReq.APIOrderID) b.SubReq = sub.SubPayReq } else { return } b.Sub = sub } type Sub struct { Base *base.Base Req interface{} } func (s *Sub) SubPayReq(isReplace bool) ([]byte, error) { send := s.PackReq() s.Req = send reqData := s.Base.PackReq(send) method := "POST" if s.Base.HttpType == base.HttpTypeGet { method = "GET" } req, err := http.NewRequest(method, s.Base.ReqURL, reqData) if err != nil { log.Error("err:%v", err) return nil, errors.New("pay fail") } s.Base.PackHeader(req.Header) s.Base.Status = s.Post(req) ret, err := s.GetResp() if err != nil { log.Error("err:%v", err) return nil, err } switch v := ret.(type) { case *pb.InnerRechargeResp: v.IsReplace = isReplace } data, _ := proto.Marshal(ret) return data, nil } func (s *Sub) Post(req *http.Request) int { client := &http.Client{} log.Debug("req:%+v", req) resp, err := client.Do(req) if err != nil { log.Error("http post call err:%v", err) return 1 } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) log.Debug("response Body:%v", string(body)) err = json.Unmarshal(body, &s.Base.Resp) if err != nil { log.Error("unmarshal err, %s", err.Error()) return 1 } return 0 } func marshalSortedMap(m map[string]interface{}) (string, error) { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) ordered := make(map[string]interface{}) for _, k := range keys { ordered[k] = m[k] } value, err := json.Marshal(ordered) if err != nil { return "", err } return string(value), nil } func (s *Sub) PackHeader(header http.Header) { params := make(map[string]string) now := time.Now() uuid := uuid.New().String() switch s.Req.(type) { case *WithdrawReq, *PayReq: reqData, _ := jsoniter.MarshalToString(s.Req) tmpParams := make(map[string]interface{}) err := jsoniter.UnmarshalFromString(reqData, &tmpParams) if err != nil { log.Error("unmarshal err, %s", err.Error()) return } for k, v := range tmpParams { var value string if k == "payout_info" { value, _ = jsoniter.MarshalToString(v) tmpMap := make(map[string]interface{}) _ = jsoniter.UnmarshalFromString(value, &tmpMap) value, _ = marshalSortedMap(tmpMap) params[k] = value continue } switch v.(type) { case float64: value = fmt.Sprintf("%d", int(v.(float64))) case string: value = v.(string) default: value = fmt.Sprintf("%+v", v) } params[k] = value } case *QueryPayReq, *QueryWithdrawReq: reqData, _ := jsoniter.MarshalToString(s.Req) err := jsoniter.UnmarshalFromString(reqData, ¶ms) if err != nil { log.Error("unmarshal err, %s", err.Error()) return } } header.Set("x-merchant-id", getPayInfo().ProjectCode) header.Set("x-api-key", getPayInfo().ApiKey) header.Set("x-timestamp", fmt.Sprintf("%d", now.Unix())) header.Set("x-nonce", uuid) header.Set("x-sign-type", signType) params["x-merchant-id"] = getPayInfo().ProjectCode params["x-api-key"] = getPayInfo().ApiKey params["x-timestamp"] = fmt.Sprintf("%d", now.Unix()) params["x-nonce"] = uuid params["x-sign-type"] = signType header.Set("x-signature", s.GetSign(params)) } func (s *Sub) GetSign(params map[string]string) string { switch signType { case "MD5": return s.signMD5(params) case "HMAC-SHA256": return s.signSha256(params) default: log.Error("sign type is wrong") return "" } } func (s *Sub) signMD5(params map[string]string) string { var keys []string for k := range params { if k == "Content-Type" || k == "signature" { continue } keys = append(keys, k) } sort.Strings(keys) var buf strings.Builder for i, k := range keys { if i > 0 { buf.WriteString("&") } buf.WriteString(k) buf.WriteString("=") buf.WriteString(params[k]) } // 追加密钥 buf.WriteString("&key=") buf.WriteString(getPayInfo().ApiSecret) // MD5加密并转大写 h := md5.New() h.Write([]byte(buf.String())) sign := strings.ToUpper(hex.EncodeToString(h.Sum(nil))) log.Debug("moneydealer, sign value:%s sign:%s", buf.String(), sign) return sign } func (s *Sub) signSha256(params map[string]string) string { // 按照键名排序 var keys []string for k := range params { if k == "Content-Type" || k == "signature" { continue } keys = append(keys, k) } sort.Strings(keys) // 拼接字符串 var buf strings.Builder for i, k := range keys { if i > 0 { buf.WriteString("&") } buf.WriteString(k) buf.WriteString("=") buf.WriteString(params[k]) } // 使用HMAC-SHA256计算签名 h := hmac.New(sha256.New, []byte(getPayInfo().ApiSecret)) h.Write([]byte(buf.String())) sign := base64.StdEncoding.EncodeToString(h.Sum(nil)) log.Debug("moneydealer, sign value:%s sign:%s", buf.String(), sign) return sign } func (s *Sub) PackReq() interface{} { if s.Base.Opt == base.OPTPay { return s.PackPayReq() } else if s.Base.Opt == base.OPTWithdraw { return s.PackWithdrawReq() } else if s.Base.Opt == base.OPTQueryWithdraw { return s.PackQueryWithdrawReq() } else if s.Base.Opt == base.OPTQueryPay { return s.PackQueryPayReq() } return nil } func (s *Sub) GetResp() (proto.Message, error) { log.Debug("resp:%v", s.Base.Resp) if s.Base.Opt == base.OPTPay { resp := s.Base.Resp.(*PayResp) if resp.Data.PayUrl == "" { return nil, errors.New("pay fail") } return &pb.InnerRechargeResp{ APIOrderID: resp.Data.OrderNo, URL: resp.Data.PayUrl, Channel: uint32(values.MoneydealerNativePay), }, nil } else if s.Base.Opt == base.OPTWithdraw { resp := s.Base.Resp.(*WithdrawResp) if s.Base.Status == 0 && resp.Code != 0 { return nil, fmt.Errorf("result status is %d", s.Base.Status) } return &pb.InnerWithdrawResp{ APIOrderID: resp.Data.OrderNo, Channel: uint32(values.MoneydealerNativePay), Status: uint32(s.Base.Status), }, nil } else if s.Base.Opt == base.OPTQueryWithdraw { resp := s.Base.Resp.(*QueryWithdrawResp) ret := &pb.InnerQueryWithdrawResp{Msg: resp.Message} if s.Base.Status == 0 && resp.Code != 0 { s.Base.QueryWithdrawResp.Status = base.QueryStatusUnknown return ret, nil } data := resp.Data if data.OrderNo == "" || data.MerchantOrderNo == "" { s.Base.QueryWithdrawResp.Status = base.QueryStatusUnknown return ret, nil } s.Base.QueryWithdrawResp.APIOrderID = data.OrderNo s.Base.QueryWithdrawResp.OrderID = data.MerchantOrderNo s.Base.QueryWithdrawResp.Msg = resp.Message if s.Base.Status != 200 && s.Base.Status != 0 { s.Base.QueryWithdrawResp.Status = base.QueryStatusUnknown } else { if data.Status == 2 { s.Base.QueryWithdrawResp.Status = base.QueryStatusOrderSuccess } else if data.Status == 3 || data.Status == 4 { s.Base.QueryWithdrawResp.Status = base.QueryStatusFail } else { s.Base.QueryWithdrawResp.Status = base.QueryStatusUnknown } } return ret, nil } else if s.Base.Opt == base.OPTQueryPay { resp := s.Base.Resp.(*QueryPayResp) ret := &pb.InnerQueryWithdrawResp{Msg: resp.Message} if s.Base.Status == 0 && resp.Code != 0 { s.Base.QueryPayResp.Status = base.QueryStatusUnknown return ret, nil } data := resp.Data if data.OrderNo == "" || data.MerchantOrderNo == "" { s.Base.QueryPayResp.Status = base.QueryStatusUnknown return ret, nil } s.Base.QueryPayResp.APIOrderID = data.OrderNo s.Base.QueryPayResp.OrderID = data.MerchantOrderNo s.Base.QueryPayResp.Msg = resp.Message if s.Base.Status != 200 && s.Base.Status != 0 { s.Base.QueryPayResp.Status = base.QueryStatusUnknown } else { if data.Status == 2 { s.Base.QueryPayResp.Status = base.QueryStatusOrderSuccess } else if data.Status == 3 || data.Status == 4 { s.Base.QueryPayResp.Status = base.QueryStatusFail } else { s.Base.QueryPayResp.Status = base.QueryStatusUnknown } } return ret, nil } return nil, errors.New("unknown opt") } func (s *Sub) PackPayReq() interface{} { r := s.Base.PayReq userDataStr, _ := json.Marshal(UserData{UserName: r.Name, UserEmail: r.Email, UserPhone: r.Phone}) send := &PayReq{ ProjectCode: getPayInfo().ProjectCode, MerchantOrderNo: r.OrderID, UserUid: fmt.Sprintf("%d", r.UID), Amount: fmt.Sprintf("%d", r.Amount), Currency: "INR", PaymentMode: 3, NotifyUrl: s.Base.GetPayCallbackURL(), ReturnUrl: values.GetFrontCallback(), ExtraData: string(userDataStr), Phone: r.Phone, } return send } func (s *Sub) PackWithdrawReq() interface{} { r := s.Base.WithdrawReq withdrawPayInfo := WithdrawPayInfo{ AccountType: "Bank", AccountNumber: r.CardNo, BeneficiaryName: r.Name, BeneficiaryPhone: r.Phone, BankName: r.BankName, BankBranch: r.BankBranchName, Ifsc: r.PayCode, Remark: "Withdrawal payment", } send := &WithdrawReq{ ProjectCode: getPayInfo().ProjectCode, MerchantOrderNo: r.OrderID, UserUid: fmt.Sprintf("%d", r.UID), Amount: fmt.Sprintf("%d", r.Amount), Currency: "INR", PaymentMode: 2, NotifyUrl: s.Base.GetWithdrawCallbackURL(), PayoutInfo: withdrawPayInfo, } return send } func (s *Sub) PackQueryWithdrawReq() interface{} { r := s.Base.QueryWithdrawReq send := &QueryWithdrawReq{ OrderNo: r.APIOrderID, } return send } func (s *Sub) PackQueryPayReq() interface{} { r := s.Base.QueryPayReq send := &QueryPayReq{ OrderNo: r.APIOrderID, } return send } func (s *Sub) CheckSign(str string) bool { log.Debug("callback:%v", str) params := make(map[string]interface{}) err := jsoniter.UnmarshalFromString(str, ¶ms) if err != nil { log.Error("unmarshal err, %s", err.Error()) return false } paramsStr := make(map[string]string, len(params)) for k, v := range params { switch val := v.(type) { case int64: paramsStr[k] = fmt.Sprintf("%d", val) case float64: paramsStr[k] = fmt.Sprintf("%.f", val) default: paramsStr[k] = fmt.Sprintf("%+v", val) } } if s.GetSign(paramsStr) != params["signature"] { return false } if s.Base.Opt == base.OPTPayCB { req := s.Base.CallbackReq.(*PayCallbackReq) s.Base.CallbackResp.OrderID = req.OrderNo s.Base.CallbackResp.APIOrderID = req.MerchantOrderNo s.Base.CallbackResp.Success = req.Status == 2 } else if s.Base.Opt == base.OPTWithdrawCB { req := s.Base.CallbackReq.(*WithdrawCallbackReq) s.Base.CallbackResp.OrderID = req.OrderNo s.Base.CallbackResp.APIOrderID = req.MerchantOrderNo s.Base.CallbackResp.Success = req.Status == 2 } return true }