package payment import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "math/big" "sort" "strings" "time" "github.com/ZZMarquis/gm/sm4" "github.com/tjfoc/gmsm/sm2" "github.com/tjfoc/gmsm/x509" ) // Client 支付客户端 type Client struct { cfg Config } // NewClient 创建支付客户端 func NewClient(cfg Config) *Client { return &Client{cfg: cfg} } // MapToString 将 map 转换为排序后的 key=value&key=value 格式 func MapToString(m map[string]string) string { keys := make([]string, 0, len(m)) for k := range m { keys = append(keys, k) } sort.Strings(keys) var result []string for _, k := range keys { result = append(result, fmt.Sprintf("%s=%s", k, m[k])) } return strings.Join(result, "&") } // HmacSha256To16 生成 HMAC-SHA256 签名(16进制) func HmacSha256To16(key string, data string) string { mac := hmac.New(sha256.New, []byte(key)) mac.Write([]byte(data)) encode := mac.Sum(nil) return hex.EncodeToString(encode) } // GenerateOrderNumber 生成订单号 func GenerateOrderNumber() string { datePart := time.Now().Format("20060102150405") randomPart := fmt.Sprintf("%06d", time.Now().Nanosecond()%1000000) return "SJ" + datePart + randomPart } // RandomNumber 生成指定长度的随机数字字符串 func RandomNumber(n int) string { result := make([]byte, n) for i := 0; i < n; i++ { result[i] = byte(time.Now().Nanosecond()%10 + '0') time.Sleep(1) } return string(result) } // EncryptMobile 手机银行支付参数加密 // 返回: tysdPayParams(base64), tysdEncryptKey(base64) func (c *Client) EncryptMobile(inputJson string, signStr string) (string, string, error) { defer func() { if err := recover(); err != nil { fmt.Println("加密失败", err) } }() sm4Key := generateSM4Key() // SM2 加密 SM4 密钥(使用银行公钥) bankPubKey, err := x509.ReadPublicKeyFromHex(c.cfg.BankKey) if err != nil { return "", "", fmt.Errorf("读取银行公钥失败: %v", err) } encryptKeyBytes, err := sm2.Encrypt(bankPubKey, sm4Key, nil, sm2.C1C3C2) if err != nil { return "", "", fmt.Errorf("sm2加密失败: %v", err) } encryptKey := hex.EncodeToString(encryptKeyBytes) if len(encryptKey) > 2 && encryptKey[0:2] == "04" { encryptKeyBytes = encryptKeyBytes[1:] } // SM2 签名 merchantPrivKey, err := x509.ReadPrivateKeyFromHex(c.cfg.PrivateKey) if err != nil { return "", "", fmt.Errorf("读取商户私钥失败: %v", err) } r, s, err := sm2.Sm2Sign(merchantPrivKey, []byte(signStr), []byte(c.cfg.MerchantId), rand.Reader) if err != nil { return "", "", fmt.Errorf("签名失败: %v", err) } sig := encodeBase64(rsToBytes(r, s)) // 拼接参数并 SM4 ECB 加密 param := fmt.Sprintf("%s&sign=%s", inputJson, sig) tmp, err := sm4ECBEncrypt(sm4Key, pkcs5Padding([]byte(param), 16)) if err != nil { return "", "", fmt.Errorf("sm4加密失败: %v", err) } return encodeBase64(tmp), encodeBase64(encryptKeyBytes), nil } // DecryptResponse 解密银行响应 func (c *Client) DecryptResponse(respJson string, isRequest bool) (string, error) { var reqData map[string]string if err := json.Unmarshal([]byte(respJson), &reqData); err != nil { return "", err } reqData["accessToken"] = "" var keys []string if isRequest { keys = []string{"request", "signature", "encryptKey", "accessToken"} } else { keys = []string{"response", "signature", "encryptKey", "accessToken"} } var inEncryptKey, inData, inSignature string for _, key := range keys { data, ok := reqData[key] if !ok { return "", fmt.Errorf("请求数据中不存在%s", key) } switch key { case "request", "response": inData = data case "signature": inSignature = data case "encryptKey": inEncryptKey = data } } // 验签 checked := c.verify(fmt.Sprintf("%s%s%s", inData, inEncryptKey, ""), inSignature) if !checked { return "", fmt.Errorf("签名验证失败") } // 解密 SM4 密钥 privKey, err := x509.ReadPrivateKeyFromHex(c.cfg.PrivateKey) if err != nil { return "", fmt.Errorf("读取私钥失败: %v", err) } hexEncryptKey, err := hex.DecodeString(inEncryptKey) if err != nil { return "", fmt.Errorf("解密sm4key失败: %v", err) } sm4Key, err := sm2.Decrypt(privKey, hexEncryptKey, sm2.C1C3C2) if err != nil { return "", fmt.Errorf("解密sm2key失败: %v", err) } // 解密数据 request, _ := base64.StdEncoding.DecodeString(inData) encryptedSm4Key, err := sm4.CBCDecrypt(sm4Key, getSM4IV(), request) return string(pkcs5UnPadding(encryptedSm4Key)), nil } // VerifySignature 验签 func (c *Client) VerifySignature(content, signature string) bool { return c.verify(content, signature) } func (c *Client) verify(content, signature string) bool { pubKey, err := x509.ReadPublicKeyFromHex(c.cfg.SopPublicKey) if err != nil { panic(fmt.Sprintf("pubKeyBytes sm2 ReadPublicKeyFromHex err: %v", err)) } r, s := signToRS(signature) return sm2.Sm2Verify(pubKey, []byte(content), []byte(c.cfg.MerchantId), r, s) } func signToRS(signStr string) (*big.Int, *big.Int) { sub := strings.Split(signStr, "#") if len(sub) != 2 { panic(fmt.Sprintf("err rs: %x", sub)) } r, _ := new(big.Int).SetString(sub[0], 16) s, _ := new(big.Int).SetString(sub[1], 16) return r, s } func rsToBytes(r, s *big.Int) []byte { rBytes := r.Bytes() sBytes := s.Bytes() result := make([]byte, 0, len(rBytes)+len(sBytes)) result = append(result, rBytes...) result = append(result, sBytes...) return result } func encodeBase64(data []byte) string { return base64.StdEncoding.EncodeToString(data) } func getSM4IV() []byte { return []byte("UISwD9fW6cFh9SNS") } func generateSM4Key() []byte { return make([]byte, 16) } func pkcs5Padding(ciphertext []byte, blockSize int) []byte { padding := blockSize - len(ciphertext)%blockSize padtext := make([]byte, padding) for i := 0; i < padding; i++ { padtext[i] = byte(padding) } return append(ciphertext, padtext...) } func pkcs5UnPadding(origData []byte) []byte { length := len(origData) if length == 0 { return origData } unpadding := int(origData[length-1]) if unpadding > length { return origData } return origData[:(length - unpadding)] } func sm4ECBEncrypt(key, plaintext []byte) ([]byte, error) { block, err := sm4.NewCipher(key) if err != nil { return nil, err } blockSize := block.BlockSize() plaintext = pkcs5Padding(plaintext, blockSize) ciphertext := make([]byte, len(plaintext)) for start := 0; start < len(plaintext); start += blockSize { block.Encrypt(ciphertext[start:start+blockSize], plaintext[start:start+blockSize]) } return ciphertext, nil }