印度包网
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.

452 lines
12 KiB

2 months ago
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, &params)
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, &params)
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
}