108 lines
2.8 KiB
Go
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
|
|
}
|