You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
451 lines
12 KiB
451 lines
12 KiB
package moneydealer |
|
|
|
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() ([]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 |
|
} |
|
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: |
|
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 *PayReq, *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.MoneydealerPay), |
|
}, 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.MoneydealerPay), |
|
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", |
|
NotifyUrl: s.Base.GetPayCallbackURL(), |
|
ReturnUrl: values.GetFrontCallback(), |
|
ExtraData: string(userDataStr), |
|
} |
|
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 |
|
}
|
|
|