Files
digital-psychology/apps/api/internal/wechat/phone.go
T
jackyu66gitandCursor 7155b8b53a feat(api): 接入微信登录并原生实现咨询域(ECR-049/050)
小程序可在 Go 上完成微信手机号登录、测评、预约和下单,不再反代 Java。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 00:26:29 +08:00

76 lines
2.0 KiB
Go

package wechat
import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/json"
"errors"
"strings"
)
type phonePayload struct {
PhoneNumber string `json:"phoneNumber"`
PurePhoneNumber string `json:"purePhoneNumber"`
Watermark struct {
AppID string `json:"appid"`
} `json:"watermark"`
}
// DecryptPhone decrypts WeChat getPhoneNumber encryptedData (AES-128-CBC).
func DecryptPhone(sessionKey, encryptedData, iv, expectAppID string) (string, error) {
key, err := base64.StdEncoding.DecodeString(strings.TrimSpace(sessionKey))
if err != nil || len(key) != 16 {
return "", errors.New("无效的 session_key")
}
raw, err := base64.StdEncoding.DecodeString(strings.TrimSpace(encryptedData))
if err != nil || len(raw) == 0 || len(raw)%aes.BlockSize != 0 {
return "", errors.New("无效的加密数据")
}
ivb, err := base64.StdEncoding.DecodeString(strings.TrimSpace(iv))
if err != nil || len(ivb) != aes.BlockSize {
return "", errors.New("无效的 iv")
}
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
plain := make([]byte, len(raw))
cipher.NewCBCDecrypter(block, ivb).CryptBlocks(plain, raw)
plain, err = pkcs7Unpad(plain)
if err != nil {
return "", errors.New("解密失败")
}
var p phonePayload
if err := json.Unmarshal(plain, &p); err != nil {
return "", errors.New("解密失败")
}
if expectAppID != "" && p.Watermark.AppID != "" && p.Watermark.AppID != expectAppID {
return "", errors.New("微信数据校验失败")
}
phone := strings.TrimSpace(p.PhoneNumber)
if phone == "" {
phone = strings.TrimSpace(p.PurePhoneNumber)
}
if phone == "" {
return "", errors.New("未获取到手机号")
}
return phone, nil
}
func pkcs7Unpad(b []byte) ([]byte, error) {
if len(b) == 0 {
return nil, errors.New("empty")
}
n := int(b[len(b)-1])
if n == 0 || n > len(b) {
return nil, errors.New("pad")
}
for i := 0; i < n; i++ {
if b[len(b)-1-i] != byte(n) {
return nil, errors.New("pad")
}
}
return b[:len(b)-n], nil
}