service/vendor/github.com/go-pay/gopay/wechat/v3/sign.go

228 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package wechat
import (
"crypto"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"hash"
"strings"
"time"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/pkg/util"
"github.com/go-pay/gopay/pkg/xlog"
"github.com/go-pay/gopay/pkg/xpem"
)
// Deprecated
// 推荐使用 wechat.V3VerifySignByPK()
func V3VerifySign(timestamp, nonce, signBody, sign, wxPubKeyContent string) (err error) {
publicKey, err := xpem.DecodePublicKey([]byte(wxPubKeyContent))
if err != nil {
return err
}
str := timestamp + "\n" + nonce + "\n" + signBody + "\n"
signBytes, _ := base64.StdEncoding.DecodeString(sign)
h := sha256.New()
h.Write([]byte(str))
if err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, h.Sum(nil), signBytes); err != nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, err)
}
return nil
}
// 推荐直接开启自动同步验签功能
// 微信V3 版本验签(同步)
// wxPublicKey微信平台证书公钥内容通过 client.WxPublicKeyMap() 获取,然后根据 signInfo.HeaderSerial 获取相应的公钥
func V3VerifySignByPK(timestamp, nonce, signBody, sign string, wxPublicKey *rsa.PublicKey) (err error) {
if wxPublicKey == nil || wxPublicKey.N == nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, "wxPublicKey is nil")
}
str := timestamp + "\n" + nonce + "\n" + signBody + "\n"
signBytes, _ := base64.StdEncoding.DecodeString(sign)
h := sha256.New()
h.Write([]byte(str))
if err = rsa.VerifyPKCS1v15(wxPublicKey, crypto.SHA256, h.Sum(nil), signBytes); err != nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, err)
}
return nil
}
// PaySignOfJSAPI 获取 JSAPI 支付所需要的参数
// 文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
func (c *ClientV3) PaySignOfJSAPI(appid, prepayid string) (jsapi *JSAPIPayParams, err error) {
ts := util.Int642String(time.Now().Unix())
nonceStr := util.RandomString(32)
pkg := "prepay_id=" + prepayid
_str := appid + "\n" + ts + "\n" + nonceStr + "\n" + pkg + "\n"
sign, err := c.rsaSign(_str)
if err != nil {
return nil, err
}
jsapi = &JSAPIPayParams{
AppId: appid,
TimeStamp: ts,
NonceStr: nonceStr,
Package: pkg,
SignType: SignTypeRSA,
PaySign: sign,
}
return jsapi, nil
}
// PaySignOfApp 获取 App 支付所需要的参数
// 文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_4.shtml
func (c *ClientV3) PaySignOfApp(appid, prepayid string) (app *AppPayParams, err error) {
ts := util.Int642String(time.Now().Unix())
nonceStr := util.RandomString(32)
_str := appid + "\n" + ts + "\n" + nonceStr + "\n" + prepayid + "\n"
sign, err := c.rsaSign(_str)
if err != nil {
return nil, err
}
app = &AppPayParams{
Appid: appid,
Partnerid: c.Mchid,
Prepayid: prepayid,
Package: "Sign=WXPay",
Noncestr: nonceStr,
Timestamp: ts,
Sign: sign,
}
return app, nil
}
// PaySignOfApplet 获取 小程序 支付所需要的参数
// 文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml
func (c *ClientV3) PaySignOfApplet(appid, prepayid string) (applet *AppletParams, err error) {
jsapi, err := c.PaySignOfJSAPI(appid, prepayid)
if err != nil {
return nil, err
}
applet = &AppletParams{
AppId: jsapi.AppId,
TimeStamp: jsapi.TimeStamp,
NonceStr: jsapi.NonceStr,
Package: jsapi.Package,
SignType: jsapi.SignType,
PaySign: jsapi.PaySign,
}
return applet, nil
}
// PaySignOfAppletScore 获取 小程序调起支付分 所需要的 ExtraData
// 文档https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter6_1_13.shtml
func (c *ClientV3) PaySignOfAppletScore(mchId, pkg string) (extraData *AppletScoreExtraData, err error) {
var (
buffer strings.Builder
h hash.Hash
ts = util.Int642String(time.Now().Unix())
nonceStr = util.RandomString(32)
)
buffer.WriteString("mch_id=")
buffer.WriteString(mchId)
buffer.WriteString("&nonce_str=")
buffer.WriteString(nonceStr)
buffer.WriteString("&package=")
buffer.WriteString(pkg)
buffer.WriteString("&sign_type=HMAC-SHA256")
buffer.WriteString("&timestamp=")
buffer.WriteString(ts)
buffer.WriteString("&key=")
buffer.WriteString(string(c.ApiV3Key))
h = hmac.New(sha256.New, c.ApiV3Key)
h.Write([]byte(buffer.String()))
extraData = &AppletScoreExtraData{
MchId: mchId,
TimeStamp: ts,
NonceStr: nonceStr,
Package: pkg,
SignType: "HMAC-SHA256",
Sign: strings.ToUpper(hex.EncodeToString(h.Sum(nil))),
}
return extraData, nil
}
// v3 鉴权请求Header
func (c *ClientV3) authorization(method, path string, bm gopay.BodyMap) (string, error) {
var (
jb = ""
timestamp = time.Now().Unix()
nonceStr = util.RandomString(32)
)
if bm != nil {
jb = bm.JsonBody()
}
path = strings.TrimSuffix(path, "?")
ts := util.Int642String(timestamp)
_str := method + "\n" + path + "\n" + ts + "\n" + nonceStr + "\n" + jb + "\n"
if c.DebugSwitch == gopay.DebugOn {
xlog.Debugf("Wechat_V3_SignString:\n%s", _str)
}
sign, err := c.rsaSign(_str)
if err != nil {
return "", err
}
return Authorization + ` mchid="` + c.Mchid + `",nonce_str="` + nonceStr + `",timestamp="` + ts + `",serial_no="` + c.SerialNo + `",signature="` + sign + `"`, nil
}
func (c *ClientV3) rsaSign(str string) (string, error) {
if c.privateKey == nil {
return "", errors.New("privateKey can't be nil")
}
h := sha256.New()
h.Write([]byte(str))
result, err := rsa.SignPKCS1v15(rand.Reader, c.privateKey, crypto.SHA256, h.Sum(nil))
if err != nil {
return util.NULL, fmt.Errorf("[%w]: %+v", gopay.SignatureErr, err)
}
return base64.StdEncoding.EncodeToString(result), nil
}
// 自动同步请求验签
func (c *ClientV3) verifySyncSign(si *SignInfo) (err error) {
if !c.autoSign {
return nil
}
if si == nil {
return errors.New("auto verify sign, but SignInfo is nil")
}
c.rwMu.RLock()
wxPublicKey, exist := c.SnCertMap[si.HeaderSerial]
c.rwMu.RUnlock()
if !exist {
err = c.AutoVerifySign(false)
if err != nil {
return fmt.Errorf("[get all public key err]: %v", err)
}
c.rwMu.RLock()
wxPublicKey, exist = c.SnCertMap[si.HeaderSerial]
c.rwMu.RUnlock()
if !exist {
return errors.New("auto verify sign, but public key not found")
}
}
str := si.HeaderTimestamp + "\n" + si.HeaderNonce + "\n" + si.SignBody + "\n"
signBytes, _ := base64.StdEncoding.DecodeString(si.HeaderSignature)
h := sha256.New()
h.Write([]byte(str))
if err = rsa.VerifyPKCS1v15(wxPublicKey, crypto.SHA256, h.Sum(nil), signBytes); err != nil {
return fmt.Errorf("[%w]: %v", gopay.VerifySignatureErr, err)
}
return nil
}