wechat

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2025 License: MIT Imports: 15 Imported by: 0

README

WeChat Integration Package

A comprehensive WeChat integration package for PocketBase applications, providing OAuth authentication, user management, and template message functionality.

Features

  • OAuth Authentication: Complete WeChat OAuth 2.0 flow implementation
  • User Management: Automatic user creation and management
  • Template Messages: Send WeChat template messages
  • Token Management: Automatic access token refresh and caching
  • Signature Verification: WeChat signature verification for webhooks
  • PocketBase Integration: Seamless integration with PocketBase authentication system

Installation

go get github.com/sospartan/pb-toolkit/pkg/wechat

Quick Start

1. Initialize WeChat Auth
import "github.com/sospartan/pb-toolkit/pkg/wechat"

// Create WeChat auth instance
auth := &wechat.WechatAuth{
    AppID:  "your_app_id",
    Secret: "your_app_secret",
}
2. Implement AuthHandler Interface
type MyAuthHandler struct {
    App *pocketbase.PocketBase
    Auth *wechat.WechatAuth
}

func (h *MyAuthHandler) FindAuthRecordByCode(code string) (*core.Record, error) {
    // Find existing user by WeChat code
    // Return NoAuthRecordError if not found
    return nil, wechat.NoAuthRecordError
}

func (h *MyAuthHandler) SaveSignIn(token *wechat.AccessTokenResponse, info *wechat.UserInfoResponse, code string) (*core.Record, error) {
    // Save or update user record with WeChat data
    // Return the created/updated record
    return record, nil
}

func (h *MyAuthHandler) GetAuthConfig() *wechat.WechatAuth {
    return h.Auth
}

func (h *MyAuthHandler) ModifyAuthRecord(record *core.Record) error {
    // Modify record before sending response
    // Clean sensitive fields if needed
    return nil
}
3. Set Up Routes
app.OnServe().BindFunc(func(se *core.ServeEvent) error {
    // WeChat OAuth callback route
    se.Router.GET("/auth/wechat/callback", wechat.HandleAuthResponseWithCode(authHandler))
    
    // WeChat webhook verification (optional)
    se.Router.GET("/webhook/wechat", func(c echo.Context) error {
        signature := c.QueryParam("signature")
        timestamp := c.QueryParam("timestamp")
        nonce := c.QueryParam("nonce")
        
        if auth.VerifySignature(signature, timestamp, nonce) {
            return c.String(http.StatusOK, c.QueryParam("echostr"))
        }
        return c.String(http.StatusForbidden, "Invalid signature")
    })
    
    return se.Next()
})

API Reference

WechatAuth
OAuth Authentication
// Fetch access token using authorization code
token, err := auth.FetchAccessToken(code)

// Refresh access token
newToken, err := auth.RefreshAccessToken(refreshToken)

// Fetch user information
userInfo, err := auth.FetchUserInfo(token.AccessToken, token.OpenID)
Template Messages
// Send template message
data := map[string]wechat.TemplateMessageData{
    "first":    {Value: "Hello!"},
    "keyword1": {Value: "Order #12345"},
    "keyword2": {Value: "Processing"},
    "remark":   {Value: "Thank you for your order"},
}

err := auth.SendTemplateMessage(
    "user_openid",
    "template_id",
    "https://example.com",
    data,
)
Signature Verification
// Verify WeChat signature
isValid := auth.VerifySignature(signature, timestamp, nonce)
Response Types
AccessTokenResponse
type AccessTokenResponse struct {
    AccessToken    string `json:"access_token"`
    ExpiresIn      int    `json:"expires_in"`
    RefreshToken   string `json:"refresh_token"`
    OpenID         string `json:"openid"`
    Scope          string `json:"scope"`
    IsSnapshotUser int    `json:"is_snapshotuser"`
    UnionID        string `json:"unionid"`
}
UserInfoResponse
type UserInfoResponse struct {
    OpenID     string   `json:"openid"`
    Nickname   string   `json:"nickname"`
    Sex        int      `json:"sex"`
    Province   string   `json:"province"`
    City       string   `json:"city"`
    Country    string   `json:"country"`
    HeadImgURL string   `json:"headimgurl"`
    Privilege  []string `json:"privilege"`
    UnionID    string   `json:"unionid"`
}

Usage Examples

Complete Authentication Flow
package main

import (
    "log"
    "github.com/sospartan/pb-toolkit/pkg/wechat"
    "github.com/pocketbase/pocketbase"
    "github.com/pocketbase/pocketbase/core"
)

type WeChatAuthHandler struct {
    App  *pocketbase.PocketBase
    Auth *wechat.WechatAuth
}

func (h *WeChatAuthHandler) FindAuthRecordByCode(code string) (*core.Record, error) {
    // Check if user already exists with this code
    records, err := h.App.Dao().FindRecordsByFilter("users", "wechat_code = ?", code)
    if err != nil {
        return nil, err
    }
    if len(records) == 0 {
        return nil, wechat.NoAuthRecordError
    }
    return records[0], nil
}

func (h *WeChatAuthHandler) SaveSignIn(token *wechat.AccessTokenResponse, info *wechat.UserInfoResponse, code string) (*core.Record, error) {
    // Create or update user record
    collection, err := h.App.Dao().FindCollectionByNameOrId("users")
    if err != nil {
        return nil, err
    }
    
    record := core.NewRecord(collection)
    record.Set("wechat_openid", info.OpenID)
    record.Set("wechat_nickname", info.Nickname)
    record.Set("wechat_avatar", info.HeadImgURL)
    record.Set("wechat_code", code)
    record.Set("wechat_unionid", info.UnionID)
    
    if err := h.App.Dao().SaveRecord(record); err != nil {
        return nil, err
    }
    
    return record, nil
}

func (h *WeChatAuthHandler) GetAuthConfig() *wechat.WechatAuth {
    return h.Auth
}

func (h *WeChatAuthHandler) ModifyAuthRecord(record *core.Record) error {
    // Remove sensitive fields before sending to client
    record.Unset("wechat_code")
    return nil
}

func main() {
    app := pocketbase.New()
    
    auth := &wechat.WechatAuth{
        AppID:  "your_wechat_app_id",
        Secret: "your_wechat_app_secret",
    }
    
    authHandler := &WeChatAuthHandler{
        App:  app,
        Auth: auth,
    }
    
    app.OnServe().BindFunc(func(se *core.ServeEvent) error {
        se.Router.GET("/auth/wechat/callback", wechat.HandleAuthResponseWithCode(authHandler))
        return se.Next()
    })
    
    if err := app.Start(); err != nil {
        log.Fatal(err)
    }
}
Building Authorization URL
// Build WeChat authorization URL
authURL := wechat.BuildAuthUrl(
    "your_app_id",
    "https://your-domain.com/auth/wechat/callback",
    "snsapi_userinfo", // or "snsapi_base"
    "state_parameter",
)
Sending Template Messages
// Example: Order notification
func sendOrderNotification(auth *wechat.WechatAuth, openid, orderID string) error {
    data := map[string]wechat.TemplateMessageData{
        "first":    {Value: "您的订单状态已更新"},
        "keyword1": {Value: orderID},
        "keyword2": {Value: "已发货"},
        "keyword3": {Value: time.Now().Format("2006-01-02 15:04:05")},
        "remark":   {Value: "感谢您的购买!"},
    }
    
    return auth.SendTemplateMessage(
        openid,
        "your_template_id",
        "https://your-domain.com/orders/" + orderID,
        data,
    )
}

Configuration

WeChat App Configuration
  1. Register your application at WeChat Open Platform
  2. Configure your domain in the app settings
  3. Set up template messages if needed
  4. Configure OAuth redirect URI
Environment Variables
WECHAT_APP_ID=your_app_id
WECHAT_APP_SECRET=your_app_secret

Error Handling

The package provides comprehensive error handling:

// Handle authentication errors
if err != nil {
    switch err {
    case wechat.NoAuthRecordError:
        // Handle new user registration
    default:
        // Handle other errors
        log.Printf("Authentication error: %v", err)
    }
}

Best Practices

  1. Token Caching: The package automatically caches API tokens for efficiency
  2. Error Handling: Always handle authentication errors gracefully
  3. User Data: Store only necessary user information
  4. Security: Verify signatures for webhook endpoints
  5. Rate Limiting: Respect WeChat API rate limits
  6. Template Messages: Use appropriate template message formats

Dependencies

  • github.com/pocketbase/pocketbase - PocketBase integration
  • Standard Go libraries for HTTP, JSON, and crypto operations

License

This package is part of the PocketBase Toolkit and is licensed under the MIT License.

Documentation

Overview

Package wechat provides WeChat OAuth authentication, user management, and template message functionality for PocketBase applications.

Package wechat provides WeChat OAuth authentication, user management, and template message functionality for PocketBase applications.

Index

Constants

This section is empty.

Variables

View Source
var NoAuthRecordError = errors.New("no auth record found")

NoAuthRecordError is returned when no authentication record is found for a given code

Functions

func BuildAuthUrl

func BuildAuthUrl(appID, redirectURI, scope, state string) string

BuildAuthUrl builds the WeChat OAuth authorization URL Parameters:

  • appID: The WeChat application ID
  • redirectURI: The callback URL after authorization
  • scope: The OAuth scope (e.g., "snsapi_base" or "snsapi_userinfo")
  • state: A state parameter for security

Returns the complete authorization URL

func HandleAuthResponseWithCode

func HandleAuthResponseWithCode(store AuthHandler) func(e *core.RequestEvent) error

HandleAuthResponseWithCode creates a handler function for WeChat OAuth callback This function processes the authorization code from WeChat and returns a PocketBase auth response Parameters:

  • store: An implementation of AuthHandler interface

Returns a function that can be used as a PocketBase route handler

Types

type AccessTokenResponse

type AccessTokenResponse struct {
	AccessToken    string `json:"access_token"`    // OAuth access token
	ExpiresIn      int    `json:"expires_in"`      // Token expiration time in seconds
	RefreshToken   string `json:"refresh_token"`   // Refresh token for getting new access token
	OpenID         string `json:"openid"`          // User's unique identifier
	Scope          string `json:"scope"`           // OAuth scope
	IsSnapshotUser int    `json:"is_snapshotuser"` // Whether user is a snapshot user
	UnionID        string `json:"unionid"`         // Union ID for cross-platform identification
}

AccessTokenResponse represents the response from WeChat OAuth access token request

type ApiTokenResponse

type ApiTokenResponse struct {
	AccessToken string `json:"access_token"` // API access token
	ExpiresIn   int    `json:"expires_in"`   // Token expiration time in seconds
}

ApiTokenResponse represents the response from WeChat API token request This token is used for sending template messages and other API operations

type AuthHandler

type AuthHandler interface {
	// FindAuthRecordByCode finds an existing authentication record by WeChat code
	// Return NoAuthRecordError if no record is found
	FindAuthRecordByCode(code string) (*core.Record, error)

	// Save saves or updates user authentication data
	// This method should create or update a user record with WeChat information
	Save(token *AccessTokenResponse, info *UserInfoResponse, code string) (*core.Record, error)

	// GetAuthConfig returns the WeChat authentication configuration
	GetAuthConfig() *WechatAuth

	// ModifyAuthRecord allows modification of the auth record before sending response
	// Use this to clean sensitive fields or add custom data
	ModifyAuthRecord(record *core.Record) error
}

AuthHandler defines the interface for handling WeChat authentication Implement this interface to customize authentication behavior

type RefreshTokenResponse

type RefreshTokenResponse struct {
	AccessToken  string `json:"access_token"`  // New OAuth access token
	ExpiresIn    int    `json:"expires_in"`    // Token expiration time in seconds
	RefreshToken string `json:"refresh_token"` // New refresh token
	OpenID       string `json:"openid"`        // User's unique identifier
	Scope        string `json:"scope"`         // OAuth scope
}

RefreshTokenResponse represents the response from WeChat refresh token request

type TemplateMessageData

type TemplateMessageData struct {
	Value string `json:"value"` // The value to display in the template
}

TemplateMessageData represents a single data field in a template message

type TemplateMessageRequest

type TemplateMessageRequest struct {
	ToUser     string                         `json:"touser"`      // Recipient's OpenID
	TemplateID string                         `json:"template_id"` // Template message ID
	URL        string                         `json:"url"`         // URL to open when message is clicked
	Data       map[string]TemplateMessageData `json:"data"`        // Template data
}

TemplateMessageRequest represents the request to send a template message

type TemplateMessageResponse

type TemplateMessageResponse struct {
	Errcode int    `json:"errcode"` // Error code (0 means success)
	Errmsg  string `json:"errmsg"`  // Error message
	MsgID   int    `json:"msgid"`   // Message ID if successful
}

TemplateMessageResponse represents the response from WeChat template message API

type UserInfoResponse

type UserInfoResponse struct {
	OpenID     string   `json:"openid"`     // User's unique identifier
	Nickname   string   `json:"nickname"`   // User's nickname
	Sex        int      `json:"sex"`        // User's gender (1=male, 2=female, 0=unknown)
	Province   string   `json:"province"`   // User's province
	City       string   `json:"city"`       // User's city
	Country    string   `json:"country"`    // User's country
	HeadImgURL string   `json:"headimgurl"` // User's avatar URL
	Privilege  []string `json:"privilege"`  // User's privileges
	UnionID    string   `json:"unionid"`    // Union ID for cross-platform identification
}

UserInfoResponse represents the response from WeChat user info request

type WechatAuth

type WechatAuth struct {
	AppID  string // WeChat application ID
	Secret string // WeChat application secret
}

WechatAuth represents a WeChat application configuration with AppID and Secret

func (*WechatAuth) FetchAccessToken

func (w *WechatAuth) FetchAccessToken(code string) (*AccessTokenResponse, error)

FetchAccessToken exchanges the authorization code for an access token Parameters:

  • code: The authorization code from WeChat OAuth callback

Returns the access token response or an error if the request fails

func (*WechatAuth) FetchApiToken

func (w *WechatAuth) FetchApiToken() (*ApiTokenResponse, error)

FetchApiToken fetches a new API token from WeChat using client credentials This token is used for sending template messages and other API operations Returns the API token response or an error if the request fails

func (*WechatAuth) FetchUserInfo

func (w *WechatAuth) FetchUserInfo(token, openid string) (*UserInfoResponse, error)

FetchUserInfo retrieves user information using access token and openid Parameters:

  • token: The OAuth access token
  • openid: The user's OpenID

Returns the user info response or an error if the request fails

func (*WechatAuth) RefreshAccessToken

func (w *WechatAuth) RefreshAccessToken(refreshToken string) (*RefreshTokenResponse, error)

RefreshAccessToken refreshes an expired access token using the refresh token Parameters:

  • refreshToken: The refresh token from previous OAuth flow

Returns the new access token response or an error if the request fails

func (*WechatAuth) SendTemplateMessage

func (w *WechatAuth) SendTemplateMessage(openid, templateId, url string, data map[string]TemplateMessageData) error

SendTemplateMessage sends a template message to a WeChat user Parameters:

  • openid: The recipient's OpenID
  • templateId: The template message ID
  • url: The URL to open when the message is clicked
  • data: The template data to fill in the message

Returns an error if the message sending fails

func (*WechatAuth) VerifySignature

func (w *WechatAuth) VerifySignature(signature, timestamp, nonce string) bool

VerifySignature verifies the WeChat signature for webhook validation Parameters:

  • signature: The signature from WeChat
  • timestamp: The timestamp from WeChat
  • nonce: The nonce from WeChat

Returns true if the signature is valid, false otherwise

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL