增加支付功能
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// =============================================================
|
||||
// 渠道敏感参数的对称加密与掩码
|
||||
//
|
||||
// yz_platform_payment_channel.config_json 中的密钥/私钥/Secret 以 AES-256-GCM 加密后存储,
|
||||
// 格式:base64( "YZP1:" + nonce + ciphertext )。
|
||||
// 密钥来源:app.conf 的 payment_secret_key(任意长度字符串,内部做 SHA-256 派生);
|
||||
// 未配置时退回内置兜底密钥(保证开箱可用),上线前务必在 app.conf 配置该值,
|
||||
// 且注意:更换密钥后,历史密文将无法解密,需要重新保存各渠道配置。
|
||||
// =============================================================
|
||||
|
||||
const configCipherPrefix = "YZP1:"
|
||||
|
||||
// fallbackSecretKey 内置兜底密钥,仅用于开发/未配置场景
|
||||
const fallbackSecretKey = "yunzer_payment_default_secret_key_v1"
|
||||
|
||||
func configKey() []byte {
|
||||
raw, _ := beego.AppConfig.String("payment_secret_key")
|
||||
if strings.TrimSpace(raw) == "" {
|
||||
raw = fallbackSecretKey
|
||||
}
|
||||
sum := sha256.Sum256([]byte(raw))
|
||||
return sum[:]
|
||||
}
|
||||
|
||||
// EncryptConfig 加密渠道参数 JSON
|
||||
func EncryptConfig(plain string) (string, error) {
|
||||
if plain == "" {
|
||||
return "", nil
|
||||
}
|
||||
block, err := aes.NewCipher(configKey())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", err
|
||||
}
|
||||
ciphertext := gcm.Seal(nil, nonce, []byte(plain), nil)
|
||||
buf := append([]byte(configCipherPrefix), nonce...)
|
||||
buf = append(buf, ciphertext...)
|
||||
return base64.StdEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
// DecryptConfig 解密渠道参数 JSON;空串原样返回
|
||||
func DecryptConfig(encoded string) (string, error) {
|
||||
if encoded == "" {
|
||||
return "", nil
|
||||
}
|
||||
raw, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return "", errors.New("渠道参数不是有效的 base64")
|
||||
}
|
||||
if len(raw) < len(configCipherPrefix) || string(raw[:len(configCipherPrefix)]) != configCipherPrefix {
|
||||
return "", errors.New("渠道参数格式不正确(缺少加密前缀)")
|
||||
}
|
||||
block, err := aes.NewCipher(configKey())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
nonce := raw[len(configCipherPrefix) : len(configCipherPrefix)+gcm.NonceSize()]
|
||||
ciphertext := raw[len(configCipherPrefix)+gcm.NonceSize():]
|
||||
plain, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return "", errors.New("渠道参数解密失败(payment_secret_key 是否被更换过?)")
|
||||
}
|
||||
return string(plain), nil
|
||||
}
|
||||
|
||||
// MaskSecret 生成掩码:保留末 4 位
|
||||
func MaskSecret(v string) string {
|
||||
if v == "" {
|
||||
return ""
|
||||
}
|
||||
if len(v) <= 4 {
|
||||
return "******"
|
||||
}
|
||||
return "******" + v[len(v)-4:]
|
||||
}
|
||||
|
||||
// IsMasked 判断前端回传的值是否仍是掩码(表示「不修改原值」)
|
||||
func IsMasked(v string) bool {
|
||||
return strings.HasPrefix(v, "******")
|
||||
}
|
||||
Reference in New Issue
Block a user