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 }