Files
lone-services/pkg/wechat/pay/sign.go
T
2026-09-11 16:08:17 +08:00

108 lines
2.8 KiB
Go

package pay
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"fmt"
"net/http"
"strconv"
"strings"
"time"
)
func randomNonce(length int) (string, error) {
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
buf := make([]byte, length)
if _, err := rand.Read(buf); err != nil {
return "", err
}
for i, b := range buf {
buf[i] = chars[int(b)%len(chars)]
}
return string(buf), nil
}
func signSHA256WithRSA(message string, privateKey *rsa.PrivateKey) (string, error) {
hashed := sha256.Sum256([]byte(message))
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signature), nil
}
func (c *Client) buildAuthorization(method, requestURI string, body []byte) (string, error) {
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
nonce, err := randomNonce(32)
if err != nil {
return "", err
}
message := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n", method, requestURI, timestamp, nonce, string(body))
signature, err := signSHA256WithRSA(message, c.PrivateKey)
if err != nil {
return "", err
}
return fmt.Sprintf(
`WECHATPAY2-SHA256-RSA2048 mchid="%s",nonce_str="%s",timestamp="%s",serial_no="%s",signature="%s"`,
c.MchID, nonce, timestamp, c.CertificateSerialNo, signature,
), nil
}
func (c *Client) validateResponse(headers http.Header, body []byte) error {
timestamp := headers.Get("Wechatpay-Timestamp")
nonce := headers.Get("Wechatpay-Nonce")
signature := headers.Get("Wechatpay-Signature")
serial := headers.Get("Wechatpay-Serial")
if timestamp == "" || nonce == "" || signature == "" || serial == "" {
return fmt.Errorf("missing wechatpay response signature headers")
}
publicKey, ok := c.platformCerts[strings.ToUpper(serial)]
if !ok {
return fmt.Errorf("unknown wechatpay platform certificate serial: %s", serial)
}
message := fmt.Sprintf("%s\n%s\n%s\n", timestamp, nonce, string(body))
sigBytes, err := base64.StdEncoding.DecodeString(signature)
if err != nil {
return err
}
hashed := sha256.Sum256([]byte(message))
return rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], sigBytes)
}
func (c *Client) BuildMiniProgramPayParams(prepayID string) (*MiniProgramPayParams, error) {
if prepayID == "" {
return nil, fmt.Errorf("prepay_id is required")
}
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
nonce, err := randomNonce(32)
if err != nil {
return nil, err
}
pkg := "prepay_id=" + prepayID
message := strings.Join([]string{c.AppID, timestamp, nonce, pkg, ""}, "\n")
paySign, err := signSHA256WithRSA(message, c.PrivateKey)
if err != nil {
return nil, err
}
return &MiniProgramPayParams{
TimeStamp: timestamp,
NonceStr: nonce,
Package: pkg,
SignType: "RSA",
PaySign: paySign,
}, nil
}