parent
701b9a2068
commit
3d4f8a1b81
6 changed files with 392 additions and 0 deletions
@ -0,0 +1,184 @@ |
||||
package eanipay |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"net/http" |
||||
"server/common" |
||||
"server/modules/pay/base" |
||||
"server/modules/pay/values" |
||||
"server/pb" |
||||
"server/util" |
||||
"time" |
||||
|
||||
"github.com/gogo/protobuf/proto" |
||||
"github.com/liangdas/mqant/log" |
||||
) |
||||
|
||||
func NewSub(b *base.Base) { |
||||
sub := &Sub{ |
||||
Base: b, |
||||
} |
||||
b.HttpType = base.HttpTypeJson |
||||
b.ShouldSignUpper = true |
||||
if b.Opt == base.OPTPay { |
||||
b.Resp = new(PayResp) |
||||
b.ReqURL = baseURL + payURL |
||||
} else if b.Opt == base.OPTWithdraw { |
||||
b.Resp = new(WithdrawResp) |
||||
b.ReqURL = baseURL + withdrawURL |
||||
} 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.Resp = new(QueryWithdrawResp) |
||||
b.ReqURL = baseURL + queryWithdrawURL |
||||
} else if b.Opt == base.OPTQueryPay { // 查询
|
||||
b.Resp = new(QueryPayResp) |
||||
b.ReqURL = baseURL + queryPayURL |
||||
} else { |
||||
return |
||||
} |
||||
b.Sub = sub |
||||
} |
||||
|
||||
type Sub struct { |
||||
Base *base.Base |
||||
} |
||||
|
||||
func (s *Sub) PackHeader(header http.Header) { |
||||
header.Set("Accept", "application/json") |
||||
url := baseURL |
||||
if s.Base.Opt == base.OPTPay { |
||||
url += payURL |
||||
} else if s.Base.Opt == base.OPTWithdraw { |
||||
url += withdrawURL |
||||
} else if s.Base.Opt == base.OPTQueryWithdraw { |
||||
url += queryWithdrawURL |
||||
} else if s.Base.Opt == base.OPTQueryPay { |
||||
url += queryPayURL |
||||
} |
||||
randomStr := util.GetSimpleRandomString(10) |
||||
bodyStr, _ := json.Marshal(s.Base.ReqData) |
||||
now := time.Now().UnixMilli() |
||||
sign := Sign(string(bodyStr), fmt.Sprintf("%d", now), randomStr, url) |
||||
header.Set("Authorization", fmt.Sprintf(`V2_SHA256 appId=%s,sign=%s,timestamp=%d,nonce=%s`, appID, sign, now, randomStr)) |
||||
} |
||||
|
||||
func (s *Sub) PackReq() interface{} { |
||||
if s.Base.Opt == base.OPTPay { |
||||
return s.PackPayReq() |
||||
} else if s.Base.Opt == base.OPTWithdraw { |
||||
return s.PackWithdrawReq() |
||||
} |
||||
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.Action.URL == "" { |
||||
return nil, errors.New("pay fail") |
||||
} |
||||
return &pb.InnerRechargeResp{APIOrderID: resp.Data.PaymentNo, URL: resp.Data.Action.URL, Channel: uint32(values.EaniPay)}, nil |
||||
} else if s.Base.Opt == base.OPTWithdraw { |
||||
resp := s.Base.Resp.(*WithdrawResp) |
||||
if s.Base.Status == 0 && resp.Code != "OK" { |
||||
return nil, errors.New("withdraw fail") |
||||
} |
||||
return &pb.InnerWithdrawResp{APIOrderID: resp.Data.PayoutNo, Channel: uint32(values.EaniPay)}, nil |
||||
} |
||||
return nil, errors.New("unknown opt") |
||||
} |
||||
|
||||
func (s *Sub) PackPayReq() interface{} { |
||||
r := s.Base.PayReq |
||||
send := &PayReq{ |
||||
MerchantTradeNo: r.OrderID, |
||||
Amount: fmt.Sprintf("%d", r.Amount), |
||||
Currency: "INR", |
||||
Description: "TeenPatti", |
||||
Payer: Payer{UserID: fmt.Sprintf("%d", r.UID), Name: r.Name, Email: r.Email, Phone: r.Phone}, |
||||
PayMethod: struct { |
||||
Type string `json:"type"` // UPI
|
||||
}{Type: "UPI"}, |
||||
TradeEnv: struct { |
||||
IP string `json:"ip"` |
||||
}{IP: r.IP}, |
||||
NotifyUrl: values.GetPayCallback(values.EaniPay), |
||||
ReturnUrl: values.GetPayCallback(values.EaniPay), |
||||
} |
||||
s.Base.ReqData = send |
||||
return send |
||||
} |
||||
|
||||
func (s *Sub) PackWithdrawReq() interface{} { |
||||
r := s.Base.WithdrawReq |
||||
send := &WithdrawReq{ |
||||
MerchantTradeNo: r.OrderID, |
||||
Amount: fmt.Sprintf("%d", r.Amount), |
||||
Currency: "INR", |
||||
Description: "TeenPatti", |
||||
NotifyUrl: values.GetWithdrawCallback(values.EaniPay), |
||||
} |
||||
if r.PayType == common.WithdrawTypeBank { |
||||
send.PayoutMethod.Type = "BANK_ACCOUNT" |
||||
send.PayoutMethod.Mode = "IMPS" |
||||
send.PayoutMethod.BankCode = r.PayCode |
||||
send.PayoutMethod.BankName = r.BankName |
||||
send.PayoutMethod.AccountNumber = r.CardNo |
||||
send.PayoutMethod.PayeeName = r.Name |
||||
send.PayoutMethod.PayeePhone = r.Phone |
||||
send.PayoutMethod.PayeeEmail = r.Email |
||||
send.PayoutMethod.PayeeAddress = r.Address |
||||
} else { |
||||
return nil |
||||
} |
||||
s.Base.ReqData = send |
||||
return send |
||||
} |
||||
|
||||
func (s *Sub) CheckSign(str string) bool { |
||||
log.Debug("callback:%v", s.Base.CallbackReq) |
||||
signStr := s.Base.C.GetHeader("Authorization") |
||||
signData := parseData(signStr) |
||||
if s.Base.Opt == base.OPTPayCB { |
||||
req := s.Base.CallbackReq.(*PayCallbackReq) |
||||
s.Base.CallbackResp.OrderID = req.Data.MerchantTradeNo |
||||
if req.Data.Status == "PENDING" || req.Data.Status == "PROCESSING" { |
||||
return false |
||||
} |
||||
s.Base.CallbackResp.APIOrderID = req.Data.PaymentNo |
||||
s.Base.CallbackResp.Success = req.Data.Status == "PAID" |
||||
return Sign(str, signData.Timestamp, signData.Nonce, values.GetPayCallback(values.EaniPay)) == signData.Sign |
||||
} else if s.Base.Opt == base.OPTWithdrawCB { |
||||
req := s.Base.CallbackReq.(*WithdrawCallbackReq) |
||||
s.Base.CallbackResp.OrderID = req.Data.MerchantTradeNo |
||||
if req.Data.Status == "PENDING" || req.Data.Status == "PROCESSING" { |
||||
return false |
||||
} |
||||
s.Base.CallbackResp.Success = req.Data.Status == "PAID" |
||||
return Sign(str, signData.Timestamp, signData.Nonce, values.GetWithdrawCallback(values.EaniPay)) == signData.Sign |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func Sign(bodyStr, t, ran, url string) string { |
||||
signStr := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n%s\n", |
||||
appID, |
||||
appSecret, |
||||
"POST", |
||||
url, |
||||
t, |
||||
ran, |
||||
bodyStr, |
||||
) |
||||
sign := util.CalculateSHA256(signStr) |
||||
log.Debug("signStr:%s,sign:%s", signStr, sign) |
||||
return sign |
||||
} |
||||
@ -0,0 +1,196 @@ |
||||
package eanipay |
||||
|
||||
import "strings" |
||||
|
||||
const ( |
||||
baseURL = "https://gateway.eanishop.com" |
||||
// baseURL = "https://gateway.eanishop.com"
|
||||
payURL = "/pg/v2/payment/create" |
||||
queryPayURL = "/pg/v2/payment/query" |
||||
withdrawURL = "/pg/v2/payout/create" |
||||
queryWithdrawURL = "/pg/v2/payout/query" |
||||
appID = "c553240e3e3842ad98eb8ae23c3745b5" |
||||
appSecret = "8e77714f16d2487f9846cc6c3432ead5" |
||||
) |
||||
|
||||
type PayReq struct { |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
Description string `json:"description"` |
||||
Payer Payer `json:"payer"` |
||||
PayMethod struct { |
||||
Type string `json:"type"` // UPI
|
||||
} `json:"payMethod"` |
||||
TradeEnv struct { |
||||
IP string `json:"ip"` |
||||
} `json:"tradeEnv"` // 如果具体环境信息比较复杂,可以直接使用json.RawMessage
|
||||
MerchantAttach string `json:"merchantAttach,omitempty"` // omitempty表示如果字段为空,则在JSON中不显示
|
||||
NotifyUrl string `json:"notifyUrl"` |
||||
ReturnUrl string `json:"returnUrl"` |
||||
} |
||||
|
||||
type Payer struct { |
||||
UserID string `json:"userId" validate:"required,max=64"` |
||||
Name string `json:"name" validate:"required,max=80"` |
||||
Email string `json:"email" validate:"required,email,max=80"` |
||||
Phone string `json:"phone" validate:"required,max=20"` |
||||
} |
||||
|
||||
type PayResp struct { |
||||
Code string `json:"code"` |
||||
ErrorMessage string `json:"errorMessage"` |
||||
Data PaymentData `json:"data"` |
||||
} |
||||
|
||||
type PaymentData struct { |
||||
PaymentNo string `json:"paymentNo"` |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
Action Action `json:"action"` |
||||
} |
||||
|
||||
type Action struct { |
||||
PayMethodType string `json:"payMethodType"` |
||||
Type string `json:"type"` |
||||
URL string `json:"url"` |
||||
Method string `json:"method"` |
||||
} |
||||
|
||||
type PayCallbackReq struct { |
||||
Event string `json:"event"` |
||||
Code string `json:"code"` |
||||
ErrorMessage string `json:"errorMessage"` |
||||
Data struct { |
||||
PaymentNo string `json:"paymentNo"` |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
RefundStatus string `json:"refundStatus"` |
||||
Status string `json:"status"` // PENDING 待⽀付 PROCESSING ⽀付中 PAID ⽀付成功 FAILURE ⽀付失败 CANCELLED 已取消
|
||||
MerchantAttach string `json:"merchantAttach"` |
||||
CreatedTime string `json:"createdTime"` |
||||
PaidTime string `json:"paidTime"` |
||||
} `json:"data"` |
||||
} |
||||
|
||||
type QueryPayReq struct { |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
} |
||||
|
||||
type QueryPayResp struct { |
||||
Code string `json:"code"` |
||||
ErrorMessage string `json:"errorMessage"` |
||||
Data struct { |
||||
PaymentNo string `json:"paymentNo"` |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
RefundStatus string `json:"refundStatus"` |
||||
Status string `json:"status"` // PENDING 待⽀付 PROCESSING ⽀付中 PAID ⽀付成功 FAILURE ⽀付失败 CANCELLED 已取消
|
||||
MerchantAttach string `json:"merchantAttach"` |
||||
CreatedTime string `json:"createdTime"` |
||||
PaidTime string `json:"paidTime"` |
||||
} `json:"data"` |
||||
} |
||||
|
||||
type WithdrawReq struct { |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
Description string `json:"description"` |
||||
PayoutMethod struct { |
||||
Type string `json:"type"` // BANK_ACCOUNT
|
||||
Mode string `json:"mode"` // IMPS
|
||||
BankCode string `json:"bankCode"` |
||||
BankName string `json:"bankName"` |
||||
AccountNumber string `json:"accountNumber"` |
||||
PayeeName string `json:"payeeName"` |
||||
PayeePhone string `json:"payeePhone"` |
||||
PayeeEmail string `json:"payeeEmail"` |
||||
PayeeAddress string `json:"payeeAddress"` |
||||
// VPA string `json:"vpa"`
|
||||
} `json:"payoutMethod"` |
||||
MerchantAttach string `json:"merchantAttach,omitempty"` |
||||
NotifyUrl string `json:"notifyUrl"` |
||||
} |
||||
|
||||
type WithdrawResp struct { |
||||
Code string `json:"code"` // OK
|
||||
ErrorMessage string `json:"errorMessage"` |
||||
Data struct { |
||||
PayoutNo string `json:"payoutNo"` |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
} `json:"data"` |
||||
} |
||||
|
||||
type WithdrawCallbackReq struct { |
||||
Event string `json:"event"` |
||||
Code string `json:"code"` |
||||
ErrorMessage string `json:"errorMessage"` |
||||
Data struct { |
||||
PayoutNo string `json:"payoutNo"` |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
TotalFee string `json:"totalFee"` |
||||
Status string `json:"status"` // PENDING 待处理 PROCESSING 处理中 PAID 代付成功 FAILURE 代付失败 REVERSED 代付退票 CANCELLED 代付取消
|
||||
MerchantAttach string `json:"merchantAttach"` |
||||
CreatedTime string `json:"createdTime"` |
||||
PaidTime string `json:"paidTime"` |
||||
} `json:"data"` |
||||
} |
||||
|
||||
type QueryWithdrawReq struct { |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
} |
||||
|
||||
type QueryWithdrawResp struct { |
||||
Code string `json:"code"` |
||||
ErrorMessage string `json:"errorMessage"` |
||||
Data struct { |
||||
PayoutNo string `json:"payoutNo"` |
||||
MerchantTradeNo string `json:"merchantTradeNo"` |
||||
Amount string `json:"amount"` |
||||
Currency string `json:"currency"` |
||||
TotalFee string `json:"totalFee"` |
||||
Status string `json:"status"` // PENDING 待处理 PROCESSING 处理中 PAID 代付成功 FAILURE 代付失败 REVERSED 代付退票 CANCELLED 代付取消
|
||||
MerchantAttach string `json:"merchantAttach"` |
||||
CreatedTime string `json:"createdTime"` |
||||
PaidTime string `json:"paidTime"` |
||||
} `json:"data"` |
||||
} |
||||
|
||||
type SignData struct { |
||||
AppId string |
||||
Sign string |
||||
Timestamp string |
||||
Nonce string |
||||
} |
||||
|
||||
func parseData(input string) SignData { |
||||
input = strings.TrimPrefix(input, "V2_SHA256 ") |
||||
pairs := strings.Split(input, ",") |
||||
data := SignData{} |
||||
for _, pair := range pairs { |
||||
keyValue := strings.Split(pair, "=") |
||||
if len(keyValue) == 2 { |
||||
key := keyValue[0] |
||||
value := keyValue[1] |
||||
switch key { |
||||
case "appId": |
||||
data.AppId = value |
||||
case "sign": |
||||
data.Sign = value |
||||
case "timestamp": |
||||
data.Timestamp = value |
||||
case "nonce": |
||||
data.Nonce = value |
||||
} |
||||
} |
||||
} |
||||
return data |
||||
} |
||||
Loading…
Reference in new issue