修复商福通客户端加解密逻辑,交易表新增收单机构字段用来保存实际支付方式,取消交易接口实现
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
@@ -20,15 +21,17 @@ var SFTPay SftClient
|
||||
|
||||
type SftClient struct {
|
||||
appid string
|
||||
appSecret string
|
||||
privateKey *rsa.PrivateKey
|
||||
publicKey *rsa.PublicKey
|
||||
}
|
||||
|
||||
func init() {
|
||||
if !env.SftPayEnable {
|
||||
return
|
||||
}
|
||||
|
||||
SFTPay = SftClient{
|
||||
appid: env.SftPayAppId,
|
||||
appSecret: env.SftPayAppSecret,
|
||||
appid: env.SftPayAppId,
|
||||
}
|
||||
|
||||
// 加载私钥
|
||||
@@ -73,6 +76,21 @@ func init() {
|
||||
SFTPay.publicKey = publicKey
|
||||
}
|
||||
|
||||
func (s *SftClient) PaymentScanPay(req *PaymentScanPayReq) (*PaymentScanPayResp, error) {
|
||||
const url = "https://pay.rscygroup.com/api/open/payment/scanpay"
|
||||
return call[PaymentScanPayResp](s, url, req)
|
||||
}
|
||||
|
||||
func (s *SftClient) PaymentH5Pay(req *PaymentH5PayReq) (*PaymentH5PayResp, error) {
|
||||
const url = "https://pay.rscygroup.com/api/open/payment/h5pay"
|
||||
return call[PaymentH5PayResp](s, url, req)
|
||||
}
|
||||
|
||||
func (s *SftClient) OrderClose(req *OrderCloseReq) (*OrderCloseResp, error) {
|
||||
const url = "https://pay.rscygroup.com/api/open/order/close"
|
||||
return call[OrderCloseResp](s, url, req)
|
||||
}
|
||||
|
||||
type PaymentScanPayReq struct {
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
@@ -93,18 +111,56 @@ type PaymentScanPayReq struct {
|
||||
LimitPay *int `json:"limitPay"`
|
||||
}
|
||||
|
||||
type PaymentH5PayReq struct {
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
Amount int64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
PayType SftPayType `json:"payType"`
|
||||
ClientIp string `json:"clientIp"`
|
||||
MchOrderNo string `json:"mchOrderNo"`
|
||||
StoreId *string `json:"storeId"`
|
||||
RouteNo *string `json:"routeNo"`
|
||||
HbFqNum *int `json:"hbFqNum"`
|
||||
HbFqPercent *int `json:"hbFqPercent"`
|
||||
BuyerRemark *string `json:"buyerRemark"`
|
||||
NotifyUrl *string `json:"notifyUrl"`
|
||||
ReturnUrl *string `json:"returnUrl"`
|
||||
ExpiredTime *int `json:"expiredTime"`
|
||||
OrderTimeout *string `json:"orderTimeout"`
|
||||
ExtParam *string `json:"extParam"`
|
||||
LimitPay *int `json:"limitPay"`
|
||||
}
|
||||
|
||||
type OrderCloseReq struct {
|
||||
MchOrderNo *string `json:"mchOrderNo"`
|
||||
PayOrderId *string `json:"payOrderId"`
|
||||
}
|
||||
|
||||
// type OrderRefundReq struct {
|
||||
// mchRefundNo
|
||||
// payOrderId
|
||||
// mchOrderNo
|
||||
// refundReason
|
||||
// refundAmount
|
||||
// notifyUrl
|
||||
// extParam
|
||||
// }
|
||||
|
||||
type PaymentScanPayResp struct {
|
||||
Amount int64 `json:"amount"`
|
||||
MchOrderNo string `json:"mchOrderNo"`
|
||||
PayOrderId string `json:"payOrderId"`
|
||||
MercNo string `json:"mercNo"`
|
||||
ChannelSendNo *string `json:"channelSendNo"`
|
||||
ChannelTradeNo *string `json:"channelTradeNo"`
|
||||
State string `json:"state"`
|
||||
PayType string `json:"payType"`
|
||||
IfCode string `json:"ifCode"`
|
||||
ExtParam *string `json:"extParam"`
|
||||
PayInfo *string `json:"payInfo"`
|
||||
Amount int64 `json:"amount"`
|
||||
MchOrderNo string `json:"mchOrderNo"`
|
||||
PayOrderId string `json:"payOrderId"`
|
||||
MercNo string `json:"mercNo"`
|
||||
ChannelSendNo *string `json:"channelSendNo"`
|
||||
ChannelTradeNo *string `json:"channelTradeNo"`
|
||||
State string `json:"state"`
|
||||
PayType SftPayType `json:"payType"`
|
||||
IfCode string `json:"ifCode"`
|
||||
ExtParam *string `json:"extParam"`
|
||||
PayInfo *struct {
|
||||
QrCodeUrl *string `json:"qrCodeUrl"`
|
||||
} `json:"payInfo"`
|
||||
Note *string `json:"note"`
|
||||
TradeFee *int64 `json:"tradeFee"`
|
||||
StoreId *string `json:"storeId"`
|
||||
@@ -116,44 +172,20 @@ type PaymentScanPayResp struct {
|
||||
SettlementType *string `json:"settlementType"`
|
||||
}
|
||||
|
||||
func (s *SftClient) PaymentScanPay(req *PaymentScanPayResp) (*PaymentScanPayResp, error) {
|
||||
const url = "https://pay.rscygroup.com/api/open/payment/scanpay"
|
||||
return call[PaymentScanPayResp](s, url, req)
|
||||
}
|
||||
|
||||
type PaymentH5PayReq struct {
|
||||
Subject string `json:"subject"`
|
||||
Body string `json:"body"`
|
||||
Amount int64 `json:"amount"`
|
||||
Currency string `json:"currency"`
|
||||
PayType string `json:"payType"`
|
||||
ClientIp string `json:"clientIp"`
|
||||
MchOrderNo string `json:"mchOrderNo"`
|
||||
StoreId *string `json:"storeId"`
|
||||
RouteNo *string `json:"routeNo"`
|
||||
HbFqNum *int `json:"hbFqNum"`
|
||||
HbFqPercent *int `json:"hbFqPercent"`
|
||||
BuyerRemark *string `json:"buyerRemark"`
|
||||
NotifyUrl *string `json:"notifyUrl"`
|
||||
ReturnUrl *string `json:"returnUrl"`
|
||||
ExpiredTime *int `json:"expiredTime"`
|
||||
OrderTimeout *string `json:"orderTimeout"`
|
||||
ExtParam *string `json:"extParam"`
|
||||
LimitPay *int `json:"limitPay"`
|
||||
}
|
||||
|
||||
type PaymentH5PayResp struct {
|
||||
Amount int64 `json:"amount"`
|
||||
MchOrderNo string `json:"mchOrderNo"`
|
||||
PayOrderId string `json:"payOrderId"`
|
||||
MercNo string `json:"mercNo"`
|
||||
ChannelSendNo *string `json:"channelSendNo"`
|
||||
ChannelTradeNo *string `json:"channelTradeNo"`
|
||||
State string `json:"state"`
|
||||
PayType string `json:"payType"`
|
||||
IfCode string `json:"ifCode"`
|
||||
ExtParam *string `json:"extParam"`
|
||||
PayInfo *string `json:"payInfo"`
|
||||
Amount int64 `json:"amount"`
|
||||
MchOrderNo string `json:"mchOrderNo"`
|
||||
PayOrderId string `json:"payOrderId"`
|
||||
MercNo string `json:"mercNo"`
|
||||
ChannelSendNo *string `json:"channelSendNo"`
|
||||
ChannelTradeNo *string `json:"channelTradeNo"`
|
||||
State string `json:"state"`
|
||||
PayType SftPayType `json:"payType"`
|
||||
IfCode string `json:"ifCode"`
|
||||
ExtParam *string `json:"extParam"`
|
||||
PayInfo *struct {
|
||||
PayUrl *string `json:"payUrl"`
|
||||
} `json:"payInfo"`
|
||||
Note *string `json:"note"`
|
||||
TradeFee *int64 `json:"tradeFee"`
|
||||
StoreId *string `json:"storeId"`
|
||||
@@ -165,9 +197,12 @@ type PaymentH5PayResp struct {
|
||||
SettlementType *string `json:"settlementType"`
|
||||
}
|
||||
|
||||
func (s *SftClient) CreateOrderByRedirect(req *PaymentH5PayReq) (*PaymentH5PayResp, error) {
|
||||
const url = "https://pay.rscygroup.com/api/open/payment/h5pay"
|
||||
return call[PaymentH5PayResp](s, url, req)
|
||||
type OrderCloseResp struct {
|
||||
MchOrderNo string `json:"mchOrderNo"`
|
||||
PayOrderId string `json:"payOrderId"`
|
||||
MercNo string `json:"mercNo"`
|
||||
Amount int64 `json:"amount"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
func call[T any](s *SftClient, url string, req any) (*T, error) {
|
||||
@@ -231,7 +266,6 @@ func call[T any](s *SftClient, url string, req any) (*T, error) {
|
||||
|
||||
func (s *SftClient) sign(msg any) (*request, error) {
|
||||
|
||||
// 处理请求正文
|
||||
bytes, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("格式化加密正文失败:%w", err)
|
||||
@@ -246,12 +280,13 @@ func (s *SftClient) sign(msg any) (*request, error) {
|
||||
BizData: string(bytes),
|
||||
}
|
||||
|
||||
encrypted, err := rsa.SignPKCS1v15(rand.Reader, s.privateKey, crypto.SHA256, []byte(body.String(s.appSecret)))
|
||||
hashed := sha256.Sum256([]byte(body.String()))
|
||||
signature, err := rsa.SignPKCS1v15(nil, s.privateKey, crypto.SHA256, hashed[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("签名失败:%w", err)
|
||||
}
|
||||
|
||||
body.Sign = string(encrypted)
|
||||
body.Sign = string(signature)
|
||||
return &body, nil
|
||||
}
|
||||
|
||||
@@ -264,7 +299,9 @@ func (s *SftClient) verify(str []byte) (*response, error) {
|
||||
}
|
||||
|
||||
if resp.Sign != nil || resp.SignType != nil || resp.BizData != nil {
|
||||
err := rsa.VerifyPKCS1v15(s.publicKey, crypto.SHA256, str, []byte(s.appSecret))
|
||||
|
||||
hashed := sha256.Sum256([]byte(resp.String()))
|
||||
err := rsa.VerifyPKCS1v15(s.publicKey, crypto.SHA256, hashed[:], []byte(*resp.Sign))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("验签失败:%w", err)
|
||||
}
|
||||
@@ -283,10 +320,10 @@ type request struct {
|
||||
BizData string `json:"bizData"`
|
||||
}
|
||||
|
||||
func (r request) String(secret string) string {
|
||||
func (r request) String() string {
|
||||
return fmt.Sprintf(
|
||||
"appId=%s&bizData=%s&reqId=%s&reqTime=%s&signType=%s&version=%s&appSecret=%s",
|
||||
r.AppId, r.BizData, r.ReqId, r.ReqTime, r.SignType, r.Version, secret,
|
||||
"appId=%s&bizData=%s&reqId=%s&reqTime=%s&signType=%s&version=%s",
|
||||
r.AppId, r.BizData, r.ReqId, r.ReqTime, r.SignType, r.Version,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -298,3 +335,18 @@ type response struct {
|
||||
SignType *string `json:"signType"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
}
|
||||
|
||||
func (r response) String() string {
|
||||
return fmt.Sprintf(
|
||||
"bizData=%s&code=%s&msg=%s&signType=%s×tamp=%s",
|
||||
u.Z(r.BizData), r.Code, u.Z(r.Msg), u.Z(r.SignType), r.Timestamp,
|
||||
)
|
||||
}
|
||||
|
||||
type SftPayType string
|
||||
|
||||
const (
|
||||
SftAlipay SftPayType = "ALIPAY"
|
||||
SftWeChat SftPayType = "WECHAT"
|
||||
SftUnionPay SftPayType = "UNIONPAY"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user