Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Wechat Official Account Oauth2 Bundle Laravel Package

tourze/wechat-official-account-oauth2-bundle

A Symfony bundle from Tourze that adds WeChat Official Account OAuth2 authentication to your app. It streamlines the authorization flow, letting users sign in with their WeChat accounts while handling token exchange and profile retrieval through configurable services and standard Symfony integration...

View on GitHub
Deep Wiki
Context7

Getting Started

This package targets WeChat 公众号 (Official Account) OAuth2 — the snsapi_base / snsapi_userinfo web authorization flow, not the QR/scan login and not the global cgi-bin/token API. At 0.0.1 treat it as pre-release: the changelog entry is empty, so there is no documented API contract yet.

  1. Install and pin. composer require tourze/wechat-official-account-oauth2-bundle:0.0.1 — pin the exact tag, since a 0.0.x bump is allowed to break the public API under semver.
  2. Read src/ before the README. The distribution is published as a bundle (Symfony DI convention). There is very likely no WechatOAuthServiceProvider or config/wechat.php for Laravel. Find the concrete OAuth client class and its constructor signature — that tells you whether you get a ready-made service or need to instantiate it yourself with appid / secret / an HTTP client.
  3. Wire it manually if needed. If no provider ships, register a singleton in AppServiceProvider binding the client from config('services.wechat_mp') and injecting Laravel's Illuminate\Http\Client\Factory. Ten lines, and it makes the rest of the integration testable.
  4. Register the callback routes. You need exactly two: one that redirects into WeChat's open.weixin.qq.com/connect/oauth2/authorize endpoint, one that receives ?code=&state=. Both must be outside auth middleware — this flow is the login.
  5. First use case: silent identification inside the WeChat in-app browser. snsapi_base returns only an openid with no consent screen; that is enough to bind a session to a follower and is by far the most common day-to-day need. Add snsapi_userinfo only when you actually need nickname/avatar/UnionID.

Before writing any code, confirm two things in the MP admin console: the account is a 认证服务号 (verified service account — subscription accounts cannot use snsapi_userinfo), and your callback host is set under 设置与开发 → 公众号设置 → 网页授权域名.

Implementation Patterns

Two-route controller pair with a server-side state. Never trust state from the query string alone; stash it in the session when you build the authorize URL and compare on return. Bind the intended post-login destination to the same session entry.

public function redirect(Request $request)
{
    $state = Str::random(32);
    $request->session()->put('wechat.oauth', [
        'state' => $state,
        'intended' => $request->query('redirect', '/'),
    ]);

    return redirect()->away($this->oauth->authorizationUrl(
        redirectUri: route('wechat.oauth.callback'),
        scope: 'snsapi_base',
        state: $state,
    ));
}

Scope escalation as a two-stage flow. Do not request snsapi_userinfo on every page load — it shows a consent interstitial each time the user's authorization has lapsed, which is a real conversion killer. Pattern: silent snsapi_base on entry to capture openid, persist it against the user/session, and only fire the consent flow when the user hits a screen that genuinely needs profile data (profile completion, checkout with name prefill).

Bind openid to your users table, not to the session long-term. Add wechat_openid (and wechat_unionid if you're on Open Platform) with a unique index. First OAuth callback → look up or create → Auth::login(). This is the whole point of the package; everything downstream (notification templates, customer service messages) keys off that openid.

Cache the OAuth access token per openid, not globally. The sns/oauth2/access_token token is scoped to one user and lives ~2 hours; the refresh_token lives ~30 days. Cache key wechat:oauth:{openid} in Redis with a TTL of expires_in - 300, and refresh rather than re-running the whole redirect when you can. Do not reuse the account-wide cgi-bin/token here — the two are not interchangeable and mixing them produces baffling 40001 errors.

Multi-account support via a named config map. If you run more than one 公众号 (common: a service account plus a test account), key config by an account slug and resolve the client per request. Avoid a single global appid — retrofitting multi-tenancy after the fact means touching every call site.

Test with a faked HTTP layer. Because the flow is two outbound calls plus a browser redirect, Http::fake() on the authorize/token/userinfo endpoints gives you full coverage without a WeChat sandbox. Assert on the built authorize URL separately — that string is where the subtle bugs live (redirect_uri encoding, missing fragment).

Middleware for the WeChat browser. A small EnsureWeChatBrowser middleware checking the MicroMessenger user agent lets you fail fast with a QR-code page instead of letting users hit redirect_uri 参数错误 on desktop Safari.

Gotchas and Tips

  • This flow only works inside the WeChat in-app browser. Desktop Chrome, or WeChat's own "open in browser" handoff, will not carry the session and typically returns 10003 redirect_uri 域名与后台配置不一致. Plan a separate path (QR login or SMS) for out-of-WeChat traffic; a large share of your users will hit this.
  • redirect_uri matching is exact on host and scheme. The domain must be configured in the MP console, must not include a port (WeChat only tolerates 80/443), and the scheme must match. http vs https mismatches are the single most common cause of a failed integration.
  • WeChat returns errors with HTTP 200. Every response body must be inspected for errcode. 40029 = invalid code, 40163 = code already used, 40030 = invalid refresh token, 41008 = missing code. A naive $response->json('openid') returns null and you spend an afternoon debugging a "successful" request.
  • Authorization codes are single-use and expire in ~5 minutes. Any retry logic that replays a code will fail. Log the errcode and restart the flow instead.
  • Do not log the full callback URL. It contains code (a bearer credential for that user's token exchange) and the state. Scrub both in your request logging middleware.
  • Nicknames contain emoji and traditional characters. Store profile fields in utf8mb4 columns — a utf8 (three-byte) column will throw Incorrect string value on a meaningful fraction of real users. This bites in production, never in seeded tests.
  • UnionID is absent unless the account is bound to 开放平台. Don't design cross-app identity around it until you've verified the binding; the field simply won't appear in the response and any $data['unionid'] access will be an undefined-key warning.
  • state is your only CSRF defence. WeChat echoes it back verbatim and does nothing else with it. Also keep it short and opaque — it lands in the URL, in referrers, and in WeChat's own logs.
  • Preserve the #wechat_redirect fragment on the authorize URL. WeChat's docs require it; hand-rolled URL builders frequently drop it. Behaviour is generally fine without it, but including it avoids inconsistency across clients.
  • Local development requires a tunnel. WeChat must be able to reach your callback, so localhost is a non-starter. Use a public tunnel host that you've added as a 网页授权域名, or stub the token exchange entirely for local work. Keep a dedicated test 公众号 for this rather than reconfiguring the production domain.
  • Extension point to watch: at 0.0.1 the most likely instability is the token/cache layer. If the bundle hardcodes an in-memory or filesystem token store, wrap it behind your own interface early — that's where you'll want Redis, and it's the change most likely to be forced on you by a 0.0.2 release.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle