|
|
@@ -2,11 +2,18 @@ package msg
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
+ "crypto"
|
|
|
"crypto/hmac"
|
|
|
"crypto/md5"
|
|
|
+ "crypto/rand"
|
|
|
+ "crypto/rsa"
|
|
|
+ "crypto/sha1"
|
|
|
+ "crypto/x509"
|
|
|
"database/sql"
|
|
|
+ "encoding/base64"
|
|
|
"encoding/hex"
|
|
|
"encoding/json"
|
|
|
+ "encoding/pem"
|
|
|
"encoding/xml"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
@@ -159,6 +166,29 @@ func getMd5Sign(callbackKey string, params map[string]string) string {
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
}
|
|
|
|
|
|
+func getMd5SignXiaoqi(params map[string]string) string {
|
|
|
+ // 删除参数中的 sign 字段
|
|
|
+ delete(params, "sign_data")
|
|
|
+
|
|
|
+ // 按参数名进行升序排序
|
|
|
+ var keys []string
|
|
|
+ for key := range params {
|
|
|
+ keys = append(keys, key)
|
|
|
+ }
|
|
|
+ sort.Strings(keys)
|
|
|
+
|
|
|
+ // 拼接参数和值
|
|
|
+ var signKey strings.Builder
|
|
|
+ for _, key := range keys {
|
|
|
+ signKey.WriteString(key)
|
|
|
+ signKey.WriteString("=")
|
|
|
+ signKey.WriteString(params[key])
|
|
|
+ signKey.WriteString("&")
|
|
|
+ }
|
|
|
+
|
|
|
+ return signKey.String()
|
|
|
+}
|
|
|
+
|
|
|
func getMd5RuSign(callbackKey string, params map[string]string) string {
|
|
|
// 删除参数中的 sign 字段
|
|
|
delete(params, "sign")
|
|
|
@@ -318,25 +348,312 @@ func WebPayHwQuickNotify(c *gin.Context) {
|
|
|
}
|
|
|
webPayNotify(ntfData, float32(f64), c)
|
|
|
|
|
|
- //// 简单粗暴,直接给其他服转发,不需要确认是哪个服
|
|
|
- //payPostRouter := service.GetServiceConfig().SDKConfig.PayPostRouter
|
|
|
- //util.WarnF("paycallback payPostRouter:%v\n", payPostRouter)
|
|
|
- //// 组装转发body
|
|
|
- //params["sign"] = sign
|
|
|
- //var routerStr strings.Builder
|
|
|
- //for key, value := range params {
|
|
|
- // routerStr.WriteString(key)
|
|
|
- // routerStr.WriteString("=")
|
|
|
- // routerStr.WriteString(value)
|
|
|
- // routerStr.WriteString("&")
|
|
|
- //}
|
|
|
- //routerString := routerStr.String()
|
|
|
- //util.WarnF("paycallback routerString:%v\n", routerString)
|
|
|
- //go sendPosts(payPostRouter, routerString)
|
|
|
-
|
|
|
c.String(http.StatusOK, "SUCCESS")
|
|
|
}
|
|
|
|
|
|
+// 海外版xiaoqi回调
|
|
|
+func WebPayHwXiaoQiNotify(c *gin.Context) {
|
|
|
+ //util.DebugF("支付回调信息:%v", c.Request.PostForm)
|
|
|
+ params := make(map[string]string)
|
|
|
+ if err := c.Request.ParseForm(); err != nil {
|
|
|
+ util.InfoF("parseForm falied")
|
|
|
+ c.String(http.StatusOK, "FAILED")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ util.DebugF("支付回调信息2:%v", c.Request.PostForm)
|
|
|
+ for key, value := range c.Request.PostForm {
|
|
|
+ params[key] = value[0] // 假设每个参数只有一个值
|
|
|
+ }
|
|
|
+ info2 := params["extends_info_data"]
|
|
|
+ info := strings.ReplaceAll(info2, "\\", "")
|
|
|
+ var extras Extras
|
|
|
+ err := json.Unmarshal([]byte(info), &extras)
|
|
|
+ if err != nil {
|
|
|
+ util.ErrorF("支付回调参数解析错误:%v", err)
|
|
|
+ }
|
|
|
+ util.InfoF("xiaoqi 透传参数:%v params:%v", extras, params)
|
|
|
+ if extras.Platform == "IOS_X7" || extras.Bima == "IOS_X7" {
|
|
|
+ util.ErrorF("xiaoqi ios 支付签名认证:%v", info)
|
|
|
+ b, err2 := VerifySignature(params, "key")
|
|
|
+ if err2 != nil || !b {
|
|
|
+ util.ErrorF("签名错误%v", err2)
|
|
|
+ c.String(http.StatusOK, "FAILED")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ util.ErrorF("android 支付签名认证:%v", info)
|
|
|
+ b, err2 := VerifySignature(params, "key")
|
|
|
+ if err2 != nil || !b {
|
|
|
+ util.ErrorF("签名错误%v", err2)
|
|
|
+ c.String(http.StatusOK, "FAILED")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ myData, err3 := DecryptDataToMap(params["encryp_data"], "key")
|
|
|
+ if err3 != nil {
|
|
|
+ util.ErrorF("解析encryp_data error:%v", err3)
|
|
|
+ c.String(http.StatusOK, "FAILED")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ uid := myData["uid"]
|
|
|
+ cpOrderId := myData["game_orderid"]
|
|
|
+ if cpOrderId == "" {
|
|
|
+ if extras.CpOrderId != "" {
|
|
|
+ cpOrderId = extras.CpOrderId
|
|
|
+ } else {
|
|
|
+ util.ErrorF("mycard pay cporderId is nil extras:%v", extras)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ orderNo := myData["xiao7_goid"]
|
|
|
+ payCurrency := myData["game_currency"]
|
|
|
+ payType := myData["game_currency"]
|
|
|
+ usdAmount := myData["pay_price"]
|
|
|
+ ntfData := &WebNotifyData{}
|
|
|
+ ntfData.CpOrderId = cpOrderId
|
|
|
+ ntfData.SdkOrderId = orderNo
|
|
|
+ ntfData.PayMethod = payType
|
|
|
+ ntfData.PayCurrency = payCurrency
|
|
|
+ ntfData.PayTime = uint64(util.GetTimeSeconds())
|
|
|
+ ntfData.PayChannel = "xiaoqi"
|
|
|
+ util.WarnF("paycallback uid=%v cpOrderNo=%v orderNo=%v payAmount=%v payCurrency=%v payType=%v usdAmount=%v",
|
|
|
+ uid, cpOrderId, orderNo, usdAmount, payCurrency, payType, usdAmount)
|
|
|
+ f64, err := strconv.ParseFloat(usdAmount, 32)
|
|
|
+ if err != nil {
|
|
|
+ fmt.Println("Error:", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ webPayNotify(ntfData, float32(f64), c)
|
|
|
+
|
|
|
+ c.String(http.StatusOK, "success")
|
|
|
+}
|
|
|
+func DecryptDataToMap(encrypDataBase64 string, privateKeyStr string) (map[string]string, error) {
|
|
|
+ // 1. Base64 解码得到 raw_encryp_data
|
|
|
+ rawEncrypData, err := base64.StdEncoding.DecodeString(encrypDataBase64)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("base64解码失败: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 使用 RSA 私钥解密
|
|
|
+ decryptedData, err := rsaPrivateDecrypt(rawEncrypData, privateKeyStr)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("RSA解密失败: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 解析查询字符串为 map
|
|
|
+ return parseQueryStringToMap(string(decryptedData)), nil
|
|
|
+}
|
|
|
+
|
|
|
+// rsaPrivateDecrypt RSA私钥解密
|
|
|
+func rsaPrivateDecrypt(ciphertext []byte, privateKeyStr string) ([]byte, error) {
|
|
|
+ // 解析私钥
|
|
|
+ privateKey, err := parsePrivateKey(privateKeyStr)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ // RSA 私钥解密(PKCS1v15 填充)
|
|
|
+ plaintext, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertext)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("解密失败: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return plaintext, nil
|
|
|
+}
|
|
|
+
|
|
|
+// parsePrivateKey 解析 PEM 格式的私钥
|
|
|
+func parsePrivateKey(privateKeyStr string) (*rsa.PrivateKey, error) {
|
|
|
+ // 去除空白字符
|
|
|
+ privateKeyStr = strings.TrimSpace(privateKeyStr)
|
|
|
+
|
|
|
+ // 确保是 PEM 格式
|
|
|
+ if !strings.Contains(privateKeyStr, "-----BEGIN") {
|
|
|
+ privateKeyStr = "-----BEGIN RSA PRIVATE KEY-----\n" +
|
|
|
+ privateKeyStr +
|
|
|
+ "\n-----END RSA PRIVATE KEY-----"
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解码 PEM
|
|
|
+ block, _ := pem.Decode([]byte(privateKeyStr))
|
|
|
+ if block == nil {
|
|
|
+ return nil, fmt.Errorf("PEM解码失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析私钥(支持 PKCS1 和 PKCS8 格式)
|
|
|
+ privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
|
+ if err != nil {
|
|
|
+ // 尝试 PKCS8 格式
|
|
|
+ key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
|
+ if err != nil {
|
|
|
+ return nil, fmt.Errorf("解析私钥失败: %v", err)
|
|
|
+ }
|
|
|
+ var ok bool
|
|
|
+ privateKey, ok = key.(*rsa.PrivateKey)
|
|
|
+ if !ok {
|
|
|
+ return nil, fmt.Errorf("不是RSA私钥")
|
|
|
+ }
|
|
|
+ return privateKey, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ return privateKey, nil
|
|
|
+}
|
|
|
+
|
|
|
+// parseQueryStringToMap 解析查询字符串为 map
|
|
|
+func parseQueryStringToMap(queryStr string) map[string]string {
|
|
|
+ params := make(map[string]string)
|
|
|
+
|
|
|
+ if queryStr == "" {
|
|
|
+ return params
|
|
|
+ }
|
|
|
+
|
|
|
+ pairs := strings.Split(queryStr, "&")
|
|
|
+ for _, pair := range pairs {
|
|
|
+ if pair == "" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ kv := strings.SplitN(pair, "=", 2)
|
|
|
+ if len(kv) == 2 {
|
|
|
+ params[kv[0]] = kv[1]
|
|
|
+ } else if len(kv) == 1 {
|
|
|
+ params[kv[0]] = ""
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return params
|
|
|
+}
|
|
|
+
|
|
|
+func VerifySignature(params map[string]string, publicKeyStr string) (bool, error) {
|
|
|
+ // 1. 获取并解码 sign_data 参数
|
|
|
+ signDataBase64, ok := params["sign_data"]
|
|
|
+ if !ok {
|
|
|
+ return false, fmt.Errorf("缺少 sign_data 参数")
|
|
|
+ }
|
|
|
+
|
|
|
+ // base64_decode 得到 raw_sign_data
|
|
|
+ rawSignData, err := base64.StdEncoding.DecodeString(signDataBase64)
|
|
|
+ if err != nil {
|
|
|
+ return false, fmt.Errorf("base64解码失败: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 构建 source_str(排除 sign_data,其他参数按字典序排序)
|
|
|
+ sourceStr := buildSourceString(params)
|
|
|
+
|
|
|
+ // 3. 验证签名
|
|
|
+ err = verifyWithPublicKey(sourceStr, rawSignData, publicKeyStr)
|
|
|
+ if err != nil {
|
|
|
+ return false, err
|
|
|
+ }
|
|
|
+
|
|
|
+ return true, nil
|
|
|
+}
|
|
|
+
|
|
|
+// buildSourceString 构建查询字符串(排除 sign_data,按字典序排序)
|
|
|
+func buildSourceString(params map[string]string) string {
|
|
|
+ // 收集除 sign_data 外的所有键
|
|
|
+ keys := make([]string, 0)
|
|
|
+ for key := range params {
|
|
|
+ if key != "sign_data" {
|
|
|
+ keys = append(keys, key)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 字典序排序
|
|
|
+ sort.Strings(keys)
|
|
|
+
|
|
|
+ // 拼接成 key=value&key=value 格式
|
|
|
+ var pairs []string
|
|
|
+ for _, key := range keys {
|
|
|
+ value := params[key]
|
|
|
+ pairs = append(pairs, fmt.Sprintf("%s=%s", key, value))
|
|
|
+ }
|
|
|
+
|
|
|
+ return strings.Join(pairs, "&")
|
|
|
+}
|
|
|
+
|
|
|
+// verifyWithPublicKey 使用公钥验证签名
|
|
|
+func verifyWithPublicKey(data string, signature []byte, publicKeyStr string) error {
|
|
|
+ // 1. 解析公钥(支持多种格式)
|
|
|
+ publicKey, err := parsePublicKey(publicKeyStr)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("解析公钥失败: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 计算 SHA1 哈希
|
|
|
+ hash := sha1.Sum([]byte(data))
|
|
|
+
|
|
|
+ // 3. 验证签名
|
|
|
+ err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA1, hash[:], signature)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("签名验证失败: %v", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// parsePublicKey 解析 PEM 格式的公钥
|
|
|
+func parsePublicKey(publicKeyStr string) (*rsa.PublicKey, error) {
|
|
|
+ // 去除空白字符
|
|
|
+ publicKeyStr = strings.TrimSpace(publicKeyStr)
|
|
|
+
|
|
|
+ // 如果公钥字符串不包含 PEM 头,尝试添加
|
|
|
+ if !strings.Contains(publicKeyStr, "-----BEGIN") {
|
|
|
+ publicKeyStr = "-----BEGIN PUBLIC KEY-----\n" +
|
|
|
+ publicKeyStr +
|
|
|
+ "\n-----END PUBLIC KEY-----"
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解码 PEM
|
|
|
+ block, _ := pem.Decode([]byte(publicKeyStr))
|
|
|
+ if block == nil {
|
|
|
+ return nil, fmt.Errorf("PEM解码失败")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析公钥
|
|
|
+ pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ publicKey, ok := pubInterface.(*rsa.PublicKey)
|
|
|
+ if !ok {
|
|
|
+ return nil, fmt.Errorf("不是RSA公钥")
|
|
|
+ }
|
|
|
+
|
|
|
+ return publicKey, nil
|
|
|
+}
|
|
|
+
|
|
|
+// parsePublicKey 解析 PEM 格式的公钥
|
|
|
+//func parsePublicKey(publicKeyStr string) (*rsa.PublicKey, error) {
|
|
|
+// // 去除可能的空白字符
|
|
|
+// publicKeyStr = strings.TrimSpace(publicKeyStr)
|
|
|
+//
|
|
|
+// // 如果公钥字符串不包含 PEM 头,尝试添加
|
|
|
+// if !strings.Contains(publicKeyStr, "-----BEGIN") {
|
|
|
+// publicKeyStr = "-----BEGIN PUBLIC KEY-----\n" +
|
|
|
+// publicKeyStr +
|
|
|
+// "\n-----END PUBLIC KEY-----"
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 解码 PEM
|
|
|
+// block, _ := pem.Decode([]byte(publicKeyStr))
|
|
|
+// if block == nil {
|
|
|
+// return nil, fmt.Errorf("PEM 解码失败")
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 解析公钥
|
|
|
+// pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
|
+// if err != nil {
|
|
|
+// return nil, err
|
|
|
+// }
|
|
|
+//
|
|
|
+// publicKey, ok := pubInterface.(*rsa.PublicKey)
|
|
|
+// if !ok {
|
|
|
+// return nil, fmt.Errorf("不是 RSA 公钥")
|
|
|
+// }
|
|
|
+//
|
|
|
+// return publicKey, nil
|
|
|
+//}
|
|
|
+
|
|
|
func WebPayHwRuNotify(c *gin.Context) {
|
|
|
//util.DebugF("支付回调信息:%v", c.Request.PostForm)
|
|
|
params := make(map[string]string)
|