package server import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "github.com/shopspring/decimal" "github.com/spf13/viper" "go_zh/pkg/http" "go_zh/pkg/utils" "net/url" "os" "sync" "time" ) type WeChatServer struct { MchId string //微信支付商户号 PrivateKey string //微信支付私钥 CertFile string //公钥证书地址 KeyFile string //私钥证书地址 AppId string //公众号id AppSecret string //公众号密钥 AppletId string //小程序appid AppletKey string //小程序密钥 CertificateSerialNo string //商户API证书序列号 WechatPayPublicKeyId string //微信支付公钥ID privateKeyInfo *rsa.PrivateKey wechatPayPublicKeyInfo *rsa.PublicKey } // 小程序登录信息返回结构体 type AppletsOpenData struct { ErrorMsg string `json:"errmsg"` ErrorCode int `json:"errcode"` UnionId string `json:"unionid"` SessionKey string `json:"session_key"` Openid string `json:"openid"` } var ( weChatServerInstance *WeChatServer weChatServerOnce sync.Once ) // GetWeChatServerInstance 获取单例实例 func GetWeChatServerInstance() *WeChatServer { weChatServerOnce.Do(func() { weChatServerInstance = &WeChatServer{ MchId: viper.GetString("weChat.mchId"), PrivateKey: viper.GetString("weChat.privateKey"), CertFile: viper.GetString("weChat.certFile"), KeyFile: viper.GetString("weChat.keyFile"), AppId: viper.GetString("weChat.appId"), AppSecret: viper.GetString("weChat.appSecret"), AppletId: viper.GetString("weChat.appletId"), AppletKey: viper.GetString("weChat.appletKey"), CertificateSerialNo: viper.GetString("weChat.certificateSerialNo"), WechatPayPublicKeyId: viper.GetString("weChat.wechatPayPublicKeyId"), } privateKey, _ := LoadPrivateKeyWithPath(weChatServerInstance.KeyFile) weChatServerInstance.privateKeyInfo = privateKey wechatPayPublicKey, _ := LoadPublicKeyWithPath(weChatServerInstance.CertFile) weChatServerInstance.wechatPayPublicKeyInfo = wechatPayPublicKey }) return weChatServerInstance } // JsapiPrepayResponse 微信支付jsapi 返回信息结构体 type JsapiPrepayResponse struct { PrepayId string `json:"prepay_id,omitempty"` } // GetAppletsOpenId 微信小程序获取登录openId func (wx *WeChatServer) GetAppletsOpenId(code string) (AppletsOpenData, error) { curlUrl := "https://api.weixin.qq.com/sns/jscode2session" //发起http get请求 var appletsOpenRes AppletsOpenData requestData := http.RequestData{} err := requestData.ConstructRequest(curlUrl) if err != nil { return appletsOpenRes, err } requestData.SetData("grant_type", "authorization_code") requestData.SetData("appid", wx.AppletId) requestData.SetData("secret", wx.AppletKey) requestData.SetData("js_code", code) ret, err := requestData.GetRequest() _ = json.Unmarshal(ret, &appletsOpenRes) return appletsOpenRes, nil } // WeChatJsPay 微信公众号jsapi支付信息 func (wx *WeChatServer) WeChatJsPay(orderSn string, payMoney decimal.Decimal, notifyUrl string, boby string, wxOpenid string) { curlUrl := "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi" result := make(map[string]interface{}) amount := make(map[string]interface{}) amount["total"] = payMoney.Mul(decimal.NewFromInt(100)).IntPart() amount["currency"] = "CNY" payer := make(map[string]interface{}) payer["openid"] = wxOpenid result["appid"] = wx.AppId result["mchid"] = wx.MchId result["description"] = boby result["out_trade_no"] = orderSn result["notify_url"] = notifyUrl result["amount"] = amount result["payer"] = payer body, _ := json.Marshal(result) fmt.Println("请求信息:", string(body)) res, _ := wx.JsapiPrepay(curlUrl, orderSn, body) fmt.Println(res) } func (wx *WeChatServer) JsapiPrepay(curlUrl string, orderSn string, body []byte) (JsapiPrepayResponse, error) { //定义返回信息 var jsapiPrepayResponse JsapiPrepayResponse //解析请求url,主要用于加密请求头信息 reqUrl, err := url.Parse(curlUrl) if err != nil { return jsapiPrepayResponse, err } //初始化请求 requestData := http.RequestData{} err = requestData.ConstructRequest(curlUrl) if err != nil { return jsapiPrepayResponse, err } requestData.DataByte = body //请求json信息 //组装请求头 requestData.SetHeader("Accept", "application/json") var authorization string authorization, err = buildAuthorization(wx.MchId, orderSn, wx.CertificateSerialNo, wx.privateKeyInfo, "POST", reqUrl.RequestURI(), body) requestData.SetHeader("Authorization", authorization) var ret []byte ret, err = requestData.PostJsonRequest() if err != nil { return jsapiPrepayResponse, err } fmt.Println(string(ret)) return jsapiPrepayResponse, nil } // buildAuthorization 构建请求头中的 Authorization 信息 func buildAuthorization( mchid string, orderSn string, certificateSerialNo string, privateKey *rsa.PrivateKey, method string, canonicalURL string, body []byte, ) (string, error) { const ( SignatureMessageFormat = "%s\n%s\n%d\n%s\n%s\n" // 数字签名原文格式 // HeaderAuthorizationFormat 请求头中的 Authorization 拼接格式 HeaderAuthorizationFormat = "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"" ) nonce := utils.Md5Sign(orderSn) //随机字符串 timestamp := time.Now().Unix() //输出当前Unix时间戳 message := fmt.Sprintf(SignatureMessageFormat, method, canonicalURL, timestamp, nonce, body) signature, err := SignSHA256WithRSA(message, privateKey) if err != nil { return "", err } authorization := fmt.Sprintf( HeaderAuthorizationFormat, mchid, nonce, timestamp, certificateSerialNo, signature, ) return authorization, nil } // LoadPrivateKey 通过私钥的文本内容加载私钥 func LoadPrivateKey(privateKeyStr string) (privateKey *rsa.PrivateKey, err error) { block, _ := pem.Decode([]byte(privateKeyStr)) if block == nil { return nil, fmt.Errorf("decode private key err") } if block.Type != "PRIVATE KEY" { return nil, fmt.Errorf("the kind of PEM should be PRVATE KEY") } key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return nil, fmt.Errorf("parse private key err:%s", err.Error()) } privateKey, ok := key.(*rsa.PrivateKey) if !ok { return nil, fmt.Errorf("not a RSA private key") } return privateKey, nil } // LoadPublicKey 通过公钥的文本内容加载公钥 func LoadPublicKey(publicKeyStr string) (publicKey *rsa.PublicKey, err error) { block, _ := pem.Decode([]byte(publicKeyStr)) if block == nil { return nil, errors.New("decode public key error") } if block.Type != "PUBLIC KEY" { return nil, fmt.Errorf("the kind of PEM should be PUBLIC KEY") } key, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, fmt.Errorf("parse public key err:%s", err.Error()) } publicKey, ok := key.(*rsa.PublicKey) if !ok { return nil, fmt.Errorf("%s is not rsa public key", publicKeyStr) } return publicKey, nil } // LoadPrivateKeyWithPath 通过私钥的文件路径内容加载私钥 func LoadPrivateKeyWithPath(path string) (privateKey *rsa.PrivateKey, err error) { privateKeyBytes, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read private pem file err:%s", err.Error()) } return LoadPrivateKey(string(privateKeyBytes)) } // LoadPublicKeyWithPath 通过公钥的文件路径加载公钥 func LoadPublicKeyWithPath(path string) (publicKey *rsa.PublicKey, err error) { publicKeyBytes, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read certificate pem file err:%s", err.Error()) } return LoadPublicKey(string(publicKeyBytes)) } // SignSHA256WithRSA 通过私钥对字符串以 SHA256WithRSA 算法生成签名信息 func SignSHA256WithRSA(source string, privateKey *rsa.PrivateKey) (signature string, err error) { if privateKey == nil { return "", fmt.Errorf("private key should not be nil") } h := crypto.Hash.New(crypto.SHA256) _, err = h.Write([]byte(source)) if err != nil { return "", nil } hashed := h.Sum(nil) signatureByte, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed) if err != nil { return "", err } return base64.StdEncoding.EncodeToString(signatureByte), nil }