Compare commits

...

4 Commits

Author SHA1 Message Date
Rzy a54212d38c 解决冲突 2024-08-09 17:26:50 +08:00
Rzy bb9ff190c3 <feat>退款 2024-08-09 17:24:03 +08:00
Rzy 7ed9cb048d Merge remote-tracking branch 'origin/dev/dev1.0' into dev/dev1.0 2024-08-09 16:12:31 +08:00
Rzy d76344573d <feat>rsa加密长度限制改善 2024-08-09 16:12:13 +08:00
15 changed files with 127 additions and 98 deletions

View File

@ -66,6 +66,9 @@ const (
OrderClosed = 1407 OrderClosed = 1407
OrderFailed = 1408 OrderFailed = 1408
OrderPayed = 1409 OrderPayed = 1409
RefundOrderNotFound = 1410
OrderStatusRefundNotSupport = 1411
//请求日志 //请求日志
RequestLogErrors = 1500 RequestLogErrors = 1500
RequestLogNotFound = 1501 RequestLogNotFound = 1501
@ -141,7 +144,8 @@ var MsgZH = map[int]string{
OrderFailed: "订单支付失败,请重新发起", OrderFailed: "订单支付失败,请重新发起",
OrderPayed: "订单已支付成功,请勿重复支付", OrderPayed: "订单已支付成功,请勿重复支付",
NotifySendFail: "回调发送失败", NotifySendFail: "回调发送失败",
RefundOrderNotFound: "退款订单未找到",
OrderStatusRefundNotSupport: "订单状态不支持退款",
PrePayFail: "预支付失败", PrePayFail: "预支付失败",
} }
var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH} var MsgMap map[string]map[int]string = map[string]map[int]string{"en": MsgZH}

View File

@ -27,8 +27,9 @@ type PayReqs struct {
type RefundReqs struct { type RefundReqs struct {
PayCommonReqBody PayCommonReqBody
OutTradeNo string `json:"out_trade_no" label:"外侧商户订单号"` RefundOutTradeNo string `json:"refund_out_trade_no" label:"需要退款的外侧商户订单号"`
OrderId string `json:"order_id" label:"平台订单号"` RefundOrderId string `json:"refundOrder_id" label:"需要退款的平台订单号"`
OutTradeNo string `json:"out_trade_no" validate:"required" label:"外侧商户订单号"`
} }
type PayUrlResp struct { type PayUrlResp struct {

View File

@ -12,6 +12,8 @@ var FrontRequestMap = map[string]func() (validForm interface{}, isSaveLog bool){
} }
var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{ var FrontRequestMapBeforeDecrypt = map[string]func() interface{}{
common.FRONT_V1 + "/pay/url": func() interface{} { return new(front.RequestBody) }, common.FRONT_V1 + "/pay/url": func() interface{} { return new(front.RequestBody) },
common.FRONT_V1 + "/pay/refund": func() interface{} { return new(front.RequestBody) },
common.FRONT_V1 + "/pay/query": func() interface{} { return new(front.RequestBody) }, common.FRONT_V1 + "/pay/query": func() interface{} { return new(front.RequestBody) },
} }

View File

@ -13,7 +13,7 @@ func NewRsa(app *appmodel.App) ApiCrypt {
} }
} }
func (r *Rsa) Encrypt(data []byte) (encryptData []byte, errCode int) { func (r *Rsa) Encrypt(data string) (encryptData []byte, errCode int) {
if r.App.PublicKey == "" { if r.App.PublicKey == "" {
return nil, errorcode.AppRsaEncryptKeyNotFound return nil, errorcode.AppRsaEncryptKeyNotFound
} }

View File

@ -12,13 +12,13 @@ func NewSm2(app *appmodel.App) ApiCrypt {
} }
} }
func (r *SM2) Encrypt(data []byte) (encryptData []byte, errCode int) { func (r *SM2) Encrypt(data string) (encryptData []byte, errCode int) {
if r.App.MerchantPublicKey == "" { if r.App.MerchantPublicKey == "" {
return nil, errorcode.AppSM2EncryptKeyNotFound return nil, errorcode.AppSM2EncryptKeyNotFound
} }
encryptDataString, err := sm2.SM2Encrypt(string(data), r.App.MerchantPublicKey) encryptDataString, err := sm2.SM2Encrypt(data, r.App.MerchantPublicKey)
if err != nil { if err != nil {
return nil, errorcode.AppSM2EncryptFail return nil, errorcode.AppSM2EncryptFail
} }

View File

@ -13,11 +13,11 @@ func NewSm4(app *appmodel.App) ApiCrypt {
} }
} }
func (r *SM4) Encrypt(data []byte) (encryptData []byte, errCode int) { func (r *SM4) Encrypt(data string) (encryptData []byte, errCode int) {
if r.App.MerchantPublicKey == "" || r.App.PrivateKey == "" { if r.App.MerchantPublicKey == "" || r.App.PrivateKey == "" {
return nil, errorcode.AppSM4DecryptKeyNotFound return nil, errorcode.AppSM4DecryptKeyNotFound
} }
encryptDataString, err := sm4.Sm4Encrypt(strconv.FormatInt(r.App.Id, 10), r.App.PrivateKey, r.App.MerchantPublicKey, string(data), "", true) encryptDataString, err := sm4.Sm4Encrypt(strconv.FormatInt(r.App.Id, 10), r.App.PrivateKey, r.App.MerchantPublicKey, data, "", true)
if err != nil { if err != nil {
return nil, errorcode.AppSM4EncryptFail return nil, errorcode.AppSM4EncryptFail
} }

View File

@ -7,7 +7,7 @@ import (
type ( type (
ApiCrypt interface { ApiCrypt interface {
Encrypt(data []byte) (encryptData []byte, errCode int) Encrypt(data string) (encryptData []byte, errCode int)
Decrypt(encryptData string) (decryptData []byte, errCode int) Decrypt(encryptData string) (decryptData []byte, errCode int)
} }

View File

@ -18,15 +18,8 @@ func EnCrypt(app *appmodel.App, data interface{}) ([]byte, int) {
if err != nil { if err != nil {
return nil, errorcode.AppAesEncryptFail return nil, errorcode.AppAesEncryptFail
} }
//aesKey, err := aes.GenerateRandomStringCrypto(16)
//if err != nil { encryptData, errCode := cryptFunc(appCheck.App).Encrypt(string(dataByte))
// return "", errorcode.AppAesEncryptFail
//}
//aesData, err := aes.Encrypt(dataByte, []byte(aesKey))
//if err != nil {
// return "", errorcode.AppAesEncryptFail
//}
encryptData, errCode := cryptFunc(appCheck.App).Encrypt(dataByte)
if errCode != apicrypt.CryptNotError { if errCode != apicrypt.CryptNotError {
return nil, errCode return nil, errCode
} }
@ -46,10 +39,6 @@ func DeCrypt(app *appmodel.App, data string, aesKey string) ([]byte, int) {
if len(dataByte) == 0 { if len(dataByte) == 0 {
return nil, errorcode.AppDeEncryptFail return nil, errorcode.AppDeEncryptFail
} }
//aesData, err := aes.Decrypt(dataByte, []byte(aesKey))
//if err != nil {
// return nil, errorcode.AppAesEncryptFail
//}
return dataByte, errorcode.Success return dataByte, errorcode.Success
} }

View File

@ -56,7 +56,7 @@ func (w *Pay) CreateOrder(order_type int) {
w.PayCode = errorcode.PayChannelNotFound w.PayCode = errorcode.PayChannelNotFound
return return
} }
w.Order, w.PayCode = services.OrderCreate(&ordersmodel.Orders{ order := &ordersmodel.Orders{
MerchantId: w.PayParam.Merchant.Id, MerchantId: w.PayParam.Merchant.Id,
PayChannelId: w.PayParam.Channel.Id, PayChannelId: w.PayParam.Channel.Id,
AppId: w.PayParam.App_id, AppId: w.PayParam.App_id,
@ -66,8 +66,11 @@ func (w *Pay) CreateOrder(order_type int) {
ExtJson: w.PayParam.ExtJson, ExtJson: w.PayParam.ExtJson,
Desc: w.PayParam.Desc, Desc: w.PayParam.Desc,
Status: common.ORDER_STATUS_WAITPAY, Status: common.ORDER_STATUS_WAITPAY,
}, }
) if order_type == common.ORDER_TYPE_REFUND {
order.RefundOrderId = w.RelationOrder.Id
}
w.Order, w.PayCode = services.OrderCreate(order)
} }
func (w *Pay) PayUrl() (url string) { func (w *Pay) PayUrl() (url string) {

View File

@ -61,7 +61,9 @@ func (w *PayCheck) CheckMerchant() {
} }
func (w *PayCheck) CheckOrderPay() { func (w *PayCheck) CheckOrderPay() {
w.GetOrder() w.GetOrder(&types.OrderFindOne{
OutTradeNo: w.Reqs.OutTradeNo,
})
if w.OldOrder != nil { if w.OldOrder != nil {
switch w.OldOrder.Status { switch w.OldOrder.Status {
case common.ORDER_STATUS_CLOSE: case common.ORDER_STATUS_CLOSE:
@ -78,27 +80,33 @@ func (w *PayCheck) CheckOrderPay() {
} }
func (w *PayCheck) CheckOrderRefund() { func (w *PayCheck) CheckOrderRefund() {
w.GetOrder() w.GetOrder(&types.OrderFindOne{
OutTradeNo: w.Reqs.RefundOutTradeNo,
OrderId: w.Reqs.RefundOrderId,
})
if w.OldOrder == nil { if w.OldOrder == nil {
w.CheckCode = errorcode.OrdersNotFound w.CheckCode = errorcode.RefundOrderNotFound
return
} }
switch w.OldOrder.Status { if w.OldOrder.Status != common.ORDER_STATUS_PAYED {
case common.ORDER_STATUS_CLOSE: w.CheckCode = errorcode.OrderStatusRefundNotSupport
w.CheckCode = errorcode.OrderClosed
case common.ORDER_STATUS_FAILED:
w.CheckCode = errorcode.OrderFailed
case common.ORDER_STATUS_WAITPAY:
w.CheckCode = errorcode.OrderStatusErr
case common.ORDER_STATUS_PAYING:
w.CheckCode = errorcode.OrderStatusErr
default:
} }
return return
} }
func (w *PayCheck) GetOrder() { func (w *PayCheck) GetOrder(orderCol *types.OrderFindOne) {
cond := builder.NewCond() cond := builder.NewCond()
cond = cond.And(builder.Eq{"out_trade_no": w.Reqs.OutTradeNo}, builder.Eq{"app_id": w.AppCheck.AppId}) cond = cond.And(builder.Eq{"app_id": w.AppCheck.AppId})
if orderCol.OrderId == "" && orderCol.OutTradeNo == "" {
w.CheckCode = errorcode.OrdersNotFound
return
}
if orderCol.OrderId != "" {
cond = cond.And(builder.Eq{"order_id": orderCol.OrderId})
}
if orderCol.OutTradeNo != "" {
cond = cond.And(builder.Eq{"out_trade_no": orderCol.OutTradeNo})
}
order, code := services.OrderFindOne(&ordersmodel.Orders{}, cond) order, code := services.OrderFindOne(&ordersmodel.Orders{}, cond)
if code == errorcode.SystemError { if code == errorcode.SystemError {
w.CheckCode = code w.CheckCode = code
@ -107,5 +115,5 @@ func (w *PayCheck) GetOrder() {
if code == errorcode.Success { if code == errorcode.Success {
w.OldOrder = order w.OldOrder = order
} }
return
} }

View File

@ -47,7 +47,7 @@ func ThirdPayInfoCheck(ctx context.Context, payReq *front.PayReqs, appCheck *ser
func ThirdPayRefund(ctx context.Context, refundReq *front.RefundReqs, appCheck *services.AppCheck, ip string) (refund *thirdpay.Pay) { func ThirdPayRefund(ctx context.Context, refundReq *front.RefundReqs, appCheck *services.AppCheck, ip string) (refund *thirdpay.Pay) {
var req types.Reqs var req types.Reqs
copier.Copy(req, refundReq) copier.Copy(&req, refundReq)
check := thirdpay.NewPayCheck(&ctx, &req, appCheck, ip) check := thirdpay.NewPayCheck(&ctx, &req, appCheck, ip)
// 校验表单 // 校验表单
check.CheckPayInfo() check.CheckPayInfo()

View File

@ -5,4 +5,11 @@ import "PaymentCenter/app/http/entities/front"
type Reqs struct { type Reqs struct {
front.PayCommonReqBody front.PayCommonReqBody
OutTradeNo string `json:"out_trade_no"` OutTradeNo string `json:"out_trade_no"`
RefundOutTradeNo string `json:"refund_out_trade_no"`
RefundOrderId string `json:"refundOrder_id"`
}
type OrderFindOne struct {
OrderId string
OutTradeNo string `json:"out_trade_no"`
} }

View File

@ -20,6 +20,13 @@ func Encrypt(plaintext, key []byte) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 加密后的数据会比原文长,因为需要添加一些填充字节
// PKCS#7填充
plaintext = pkcs7Padding(plaintext, block.BlockSize())
// 创建一个cipher.BlockMode这里使用CBC模式
// 需要一个iv初始化向量它的长度和Block的块大小相同
ciphertext := make([]byte, aes.BlockSize+len(plaintext)) ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize] iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil { if _, err := io.ReadFull(rand.Reader, iv); err != nil {
@ -29,7 +36,8 @@ func Encrypt(plaintext, key []byte) ([]byte, error) {
mode := cipher.NewCBCEncrypter(block, iv) mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext) mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
return iv, nil // 返回的密文包括iv和加密后的数据
return ciphertext, nil
} }
// 解密函数 // 解密函数

View File

@ -32,7 +32,8 @@ func parseRSAPublicKeyFromPEM(pemData []byte) (*rsa.PublicKey, error) {
} }
// encrypt 使用RSA公钥加密数据 // encrypt 使用RSA公钥加密数据
func Encrypt(publicKeyPEM string, plaintext []byte) ([]byte, error) { func Encrypt(publicKeyPEM string, plaintext string) ([]byte, error) {
var encryptedData []byte
// 将PEM编码的公钥转换为[]byte // 将PEM编码的公钥转换为[]byte
pemData := []byte(publicKeyPEM) pemData := []byte(publicKeyPEM)
@ -41,14 +42,24 @@ func Encrypt(publicKeyPEM string, plaintext []byte) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
hash := sha256.New()
maxBlockSize := pubKey.Size() - 2*hash.Size() - 2
// 创建用于加密的随机填充 // 创建用于加密的随机填充
label := []byte("") // OAEP标签对于某些情况可能是非空的 label := []byte("") // OAEP标签对于某些情况可能是非空的
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pubKey, plaintext, label) for len(plaintext) > 0 {
blockSize := maxBlockSize
if len(plaintext) < maxBlockSize {
blockSize = len(plaintext)
}
block := plaintext[:blockSize]
encryptedBlock, err := rsa.EncryptOAEP(hash, rand.Reader, pubKey, []byte(block), label)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ciphertext, nil encryptedData = append(encryptedData, encryptedBlock...)
plaintext = plaintext[blockSize:]
}
return encryptedData, nil
} }
// parseRSAPrivateKeyFromPEM 解析PEM编码的RSA私钥 // parseRSAPrivateKeyFromPEM 解析PEM编码的RSA私钥
@ -80,34 +91,38 @@ func parseRSAPrivateKeyFromPEM(pemData []byte) (*rsa.PrivateKey, error) {
// decrypt 使用RSA私钥解密数据 // decrypt 使用RSA私钥解密数据
func Decrypt(privateKeyPEM string, encryptedDataBase64 string) ([]byte, error) { func Decrypt(privateKeyPEM string, encryptedDataBase64 string) ([]byte, error) {
var decryptedData []byte
// 将PEM编码的私钥转换为[]byte // 将PEM编码的私钥转换为[]byte
pemData := []byte(privateKeyPEM) pemData := []byte(privateKeyPEM)
// 解析PEM数据以获取私钥 // 解析PEM数据以获取私钥
privKey, err := parseRSAPrivateKeyFromPEM(pemData) privKey, err := parseRSAPrivateKeyFromPEM(pemData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
keySize := privKey.PublicKey.Size()
label := []byte("") // OAEP标签对于某些情况可能是非空的
hash := sha256.New()
// 将Base64编码的加密数据解码为字节切片 // 将Base64编码的加密数据解码为字节切片
encryptedData, err := base64.StdEncoding.DecodeString(encryptedDataBase64) encryptedData, err := base64.StdEncoding.DecodeString(encryptedDataBase64)
if err != nil { if err != nil {
return nil, err return nil, err
} }
for len(encryptedData) > 0 {
// 根据你的加密方式选择合适的解密函数 block := encryptedData[:keySize]
// 这里假设使用的是OAEP填充和SHA-256哈希函数 // 这里假设使用的是OAEP填充和SHA-256哈希函数
label := []byte("") // OAEP标签对于某些情况可能是非空的 decryptedBlock, err := rsa.DecryptOAEP(hash, rand.Reader, privKey, block, label)
decrypted, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privKey, encryptedData, label)
if err != nil { if err != nil {
// 如果失败可以尝试使用PKCS#1 v1.5填充 //// 如果失败可以尝试使用PKCS#1 v1.5填充
decrypted, err = rsa.DecryptPKCS1v15(rand.Reader, privKey, encryptedData) decryptedBlock, err = rsa.DecryptPKCS1v15(rand.Reader, privKey, encryptedData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
decryptedData = append(decryptedData, decryptedBlock...)
return decrypted, nil encryptedData = encryptedData[keySize:]
}
return decryptedData, nil
} }
// 生成密钥对 // 生成密钥对

View File

@ -18,7 +18,7 @@ func TestRsaEncrypt(t *testing.T) {
} }
func TestRsaDecrypt(t *testing.T) { func TestRsaDecrypt(t *testing.T) {
data := encrypt() data := "pPnAPy7v2SY9Fu0LFcQH8UBA6VQ2FCfSg3nRZdMXS7mWjBwlacKHuFnh9UhobL7mxnmMyZPP100bpjCg2kvcfOpOp3ci85p+OYWINt4Fh3qgEOTG5FUyziaagGLm882t/I36KsDTVvbMZvC5sg4gZ9JQ5yAR+nuJfr0IxI0se/iD5luV1rms1kZHggd30iXdZtbkbX7xJ4xtnIiJmZU7kL+Xmvv1rDdPLxbol65QfnM1me1IHkXJapqSBnhEEmFQyBx31vp1ccNjkza8ZWbvTPCngc1k4kvlm6lKfwsG4hMuSdXUzveDm+Oo8StAKnyVoerJ202n7Vfx1XhehineQT0TPD7bO0HCEsDXXYEWwvcax8VdzYvHk7qSbH6e154qCr4LgDRSHKwAAExinTrzxx2rtSimieBLaEpDL2v5ch45HnhjRhWTRmM61W1g6sdHaVX1mQxaXvrT4v+h+f4TbIV4r4qeGJ6rXG+yKRoYseLzyGgystoOny9P0UH15W8rWPytV2eioWT7i3Cglg04BWP9mst67LQXeFH4CA6CkwVV2w9nCHrzxX2ouYSQELUEkTlIMry2AlkZubUnupGJLmLLUyZj7pM/6cLjyAgm02/gRc4wwf7JBBq/ipmKXpkhHXWLtQDWJEZTT+ug2v9EXy5dgPNPe8ZI0MILAeipjIc="
privateKeyPEM := `-----BEGIN RSA PRIVATE KEY----- privateKeyPEM := `-----BEGIN RSA PRIVATE KEY-----
` + PRI + ` ` + PRI + `
-----END RSA PRIVATE KEY-----` -----END RSA PRIVATE KEY-----`
@ -27,13 +27,11 @@ func TestRsaDecrypt(t *testing.T) {
} }
func encrypt() string { func encrypt() string {
data := "{\"order_no\":4323455642275676219,\"order_type\":1,\"out_tread_no\":\"asdadasdas\",\"amount\":1,\"desc\":\"abc\",\"status\":2,\"create_time\":\"2024-08-07 18:36:43\"}" data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"refundOutTreadNo001\",\"amount\":1,\"desc\":\"退款\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643,\"refund_out_trade_no\":\"asdadasdas\"}"
fmt.Println(len(data))
dataJson := []byte(data)
pub := `-----BEGIN PUBLIC KEY----- pub := `-----BEGIN PUBLIC KEY-----
` + PUB + ` ` + PUB + `
-----END PUBLIC KEY-----` -----END PUBLIC KEY-----`
en, err := Encrypt(pub, dataJson) en, err := Encrypt(pub, data)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -42,9 +40,10 @@ func encrypt() string {
} }
func encryptWithAes() string { func encryptWithAes() string {
data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}" data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"refundOutTreadNo001\",\"amount\":1,\"desc\":\"退款\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643\"refund_out_trade_no\":\"asdadasdas\"}"
aes.Encrypt([]byte(data), []byte(aes.TestKey)) aes.Encrypt([]byte(data), []byte(aes.TestKey))
dataJson := []byte(data)
dataJson := base64.StdEncoding.EncodeToString([]byte(data))
pub := `-----BEGIN PUBLIC KEY----- pub := `-----BEGIN PUBLIC KEY-----
` + PUB + ` ` + PUB + `
-----END PUBLIC KEY-----` -----END PUBLIC KEY-----`
@ -58,14 +57,7 @@ func encryptWithAes() string {
// 测试生成密钥对 // 测试生成密钥对
func TestGenerateRSAKey(t *testing.T) { func TestGenerateRSAKey(t *testing.T) {
pub, pri, err := GenerateKey() pub, pri, _ := GenerateKey()
data := "{\"pay_channel_id\":8935141660703064070,\"out_trade_no\":\"asdadasdas\",\"order_type\":1,\"amount\":1,\"desc\":\"abc\",\"ext_json\":\"\",\"app_id\":5476377146882523138,\"timestamp\":53612533412643}"
dataJson := []byte(data) fmt.Println("pub:", pub, "\n", "pri:", pri)
en, err := Encrypt(pub, dataJson)
if err != nil {
panic(err)
}
content := base64.StdEncoding.EncodeToString(en)
res, err := Decrypt(pri, content)
fmt.Println("解密", string(res), err)
} }