package pay import ( "crypto" crand "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" mrand "math/rand" "os" "time" "bindbox-game/configs" ) // BuildJSAPIParams 为小程序支付构造客户端参数 // 入参:appid(微信小程序AppID)、prepayID(统一下单返回的prepay_id) // 返回:timeStamp、nonceStr、package(格式为"prepay_id=***" )、signType(固定"RSA")、paySign(RSA-SHA256签名) // 错误:当私钥读取或签名失败时返回错误 func BuildJSAPIParams(appid string, prepayID string) (timeStamp string, nonceStr string, pkg string, signType string, paySign string, err error) { cfg := configs.Get() if cfg.WechatPay.PrivateKeyPath == "" { return "", "", "", "", "", errors.New("wechat pay private key path not configured") } b, err := os.ReadFile(cfg.WechatPay.PrivateKeyPath) if err != nil { return "", "", "", "", "", err } block, _ := pem.Decode(b) if block == nil { return "", "", "", "", "", errors.New("invalid merchant private key pem") } key, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { // 兼容PKCS1 k1, e1 := x509.ParsePKCS1PrivateKey(block.Bytes) if e1 != nil { return "", "", "", "", "", err } key = k1 } rsaKey, ok := key.(*rsa.PrivateKey) if !ok { return "", "", "", "", "", errors.New("merchant private key is not RSA") } ts := fmt.Sprintf("%d", time.Now().Unix()) // 随机串32位 const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" rb := make([]byte, 32) for i := range rb { rb[i] = letters[mrand.Intn(len(letters))] } nonce := string(rb) pkg = "prepay_id=" + prepayID signType = "RSA" message := fmt.Sprintf("%s\n%s\n%s\n%s\n", appid, ts, nonce, pkg) h := sha256.Sum256([]byte(message)) sig, err := rsa.SignPKCS1v15(crand.Reader, rsaKey, crypto.SHA256, h[:]) if err != nil { return "", "", "", "", "", err } paySign = base64.StdEncoding.EncodeToString(sig) return ts, nonce, pkg, signType, paySign, nil } // ValidateConfig 校验微信支付必要配置 // 入参:无 // 返回:true表示配置齐全;false表示缺失,并附带错误信息 func ValidateConfig() (bool, error) { c := configs.Get() if c.Wechat.AppID == "" { return false, errors.New("wechat app_id missing") } if c.WechatPay.MchID == "" || c.WechatPay.SerialNo == "" || c.WechatPay.PrivateKeyPath == "" || c.WechatPay.ApiV3Key == "" { return false, errors.New("wechat pay config incomplete") } return true, nil }