|
|
@@ -1,265 +0,0 @@
|
|
|
-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
|
|
|
-}
|