| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- package wechatmini
- import (
- "context"
- "crypto/sha1"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "time"
- )
- type Client struct {
- appID string
- appSecret string
- devPrefix string
- httpClient *http.Client
- }
- type Session struct {
- AppID string
- OpenID string
- UnionID string
- SessionKey string
- }
- type code2SessionResponse struct {
- OpenID string `json:"openid"`
- SessionKey string `json:"session_key"`
- UnionID string `json:"unionid"`
- ErrCode int `json:"errcode"`
- ErrMsg string `json:"errmsg"`
- }
- func NewClient(appID, appSecret, devPrefix string) *Client {
- return &Client{
- appID: appID,
- appSecret: appSecret,
- devPrefix: devPrefix,
- httpClient: &http.Client{Timeout: 8 * time.Second},
- }
- }
- func (c *Client) ExchangeCode(ctx context.Context, code string) (*Session, error) {
- code = strings.TrimSpace(code)
- if code == "" {
- return nil, fmt.Errorf("wechat code is required")
- }
- if c.devPrefix != "" && strings.HasPrefix(code, c.devPrefix) {
- suffix := strings.TrimPrefix(code, c.devPrefix)
- if suffix == "" {
- suffix = "default"
- }
- return &Session{
- AppID: fallbackString(c.appID, "dev-mini-app"),
- OpenID: "dev_openid_" + normalizeDevID(suffix),
- UnionID: "",
- }, nil
- }
- if c.appID == "" || c.appSecret == "" {
- return nil, fmt.Errorf("wechat mini app credentials are not configured")
- }
- values := url.Values{}
- values.Set("appid", c.appID)
- values.Set("secret", c.appSecret)
- values.Set("js_code", code)
- values.Set("grant_type", "authorization_code")
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.weixin.qq.com/sns/jscode2session?"+values.Encode(), nil)
- if err != nil {
- return nil, err
- }
- resp, err := c.httpClient.Do(req)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
- var parsed code2SessionResponse
- if err := json.Unmarshal(body, &parsed); err != nil {
- return nil, err
- }
- if parsed.ErrCode != 0 {
- return nil, fmt.Errorf("wechat code2session failed: %d %s", parsed.ErrCode, parsed.ErrMsg)
- }
- if parsed.OpenID == "" {
- return nil, fmt.Errorf("wechat code2session returned empty openid")
- }
- return &Session{
- AppID: c.appID,
- OpenID: parsed.OpenID,
- UnionID: parsed.UnionID,
- SessionKey: parsed.SessionKey,
- }, nil
- }
- func normalizeDevID(value string) string {
- sum := sha1.Sum([]byte(value))
- return hex.EncodeToString(sum[:])[:16]
- }
- func fallbackString(value, fallback string) string {
- if value != "" {
- return value
- }
- return fallback
- }
|