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

Zalo Laravel Package

socialiteproviders/zalo

Zalo OAuth provider for Laravel Socialite, letting you add Zalo login to your app with a simple driver. Install via Composer, configure your app ID and secret, then authenticate users through Zalo’s OAuth flow alongside other Socialite providers.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require socialiteproviders/zalo

This assumes laravel/socialite is already installed in your app. The 5.0.0 release notes are only a compare link (4.1.0...5.0.0), so before you commit, open the diff and read composer.json — a socialiteproviders major bump almost always means a Socialite major floor (and transitively a newer PHP/Laravel). If you're on an older Laravel, pin ^4.1 and stay there rather than fighting dependency resolution. Also sanity-check that socialiteproviders/manager is pulled in — that's what SocialiteWasCalled and extendWith rely on.

1. Configure config/services.php. Keep the secret out of the file; use env only.

'zalo' => [
    'client_id'     => env('ZALO_CLIENT_ID'),
    'client_secret' => env('ZALO_CLIENT_SECRET'),
    'redirect'      => env('ZALO_REDIRECT_URI'),
],

2. Register the provider. On Laravel ≤10 this typically lives in EventServiceProvider::$listen. On Laravel 11+ (no default EventServiceProvider) either rely on the manager's auto-registration via the package discovery or add the listener explicitly in AppServiceProvider::boot():

Event::listen(function (SocialiteWasCalled $event) {
    $event->extendWith(ZaloExtendSocialite::class);
});

3. Create the Zalo app in the developer console. Note three things before leaving that screen: the App ID, the App Secret, and the exact list of scopes your app has been approved for. The provider can only request scopes the app already has — asking for one that isn't approved fails at redirect time, not at code time.

Where to look first. Your two controller actions (redirect and callback), the services.php block, and the Zalo console scope list. In practice, ~90% of first-run failures trace back to one of those three — not to the package itself.

First use case (the 15-minute path): a ZaloController@redirect that returns Socialite::driver('zalo')->redirect(), and a callback action that exchanges the code, pulls the user, and finds-or-creates a local account. Get that round trip working against a tunnel URL in local dev before touching anything else.

Implementation Patterns

The standard redirect/callback pair — this is 95% of what you'll write:

public function redirect()
{
    return Socialite::driver('zalo')->scopes(['openid', 'profile'])->redirect();
}

public function callback(Request $request)
{
    $zaloUser = Socialite::driver('zalo')->user();

    $user = User::firstOrCreate(
        ['zalo_id' => $zaloUser->getId()],
        ['name' => $zaloUser->getName()],
    );

    Auth::login($user, remember: true);

    return redirect()->intended(route('dashboard'));
}

Note the missing email above — see the nullable-email gotcha below.

Token persistence. Zalo access tokens are short-lived; the refresh token is the piece worth storing. Save access_token, refresh_token, and a computed expires_at (derive it yourself from the raw array rather than trusting whichever helper, since shape varies by provider). Check expiry before any Graph-style call. Do not assume the driver auto-refreshes — refresh support is a common gap in Socialite providers, so verify against the 5.0 source before building on it.

Account linking, not just login. Most real Zalo integrations attach Zalo to an existing user, not create a new one. Pattern:

if (Auth::check()) {
    Auth::user()->update(['zalo_id' => $zaloUser->getId()]); // let DB unique constraint catch collisions
}

Guard against a zalo_id already belonging to someone else by putting a unique index on the column and catching UniqueConstraintViolationException, rather than a SELECT-then-INSERT which races under concurrent callbacks.

Testing. Socialite has no built-in fake, so Http::fake() the token endpoint and userinfo endpoint — or bind a fake driver into the container in a test case. Cover at minimum: happy path, user cancels (error=access_denied in the query string), and state mismatch. Assert on the mapped user object fields, not just that a user was created — that's how you catch a silent field rename on upgrade.

Multi-environment. Zalo issues per-app credentials, so maintain separate apps for local/staging/prod and always drive ZALO_REDIRECT_URI from env. The local tunnel URL must be re-registered in the console every time it changes — a fixed subdomain saves considerable pain.

Gotchas and Tips

  • Read the 5.0.0 diff before upgrading. No changelog body, no migration guide, only the compare link. A major bump in this ecosystem typically means a Socialite 5 / PHP 8.2 floor and possible removal of deprecated Socialite aliases (e.g. Socialite::with() → driver(), changed getAuthUrl() semantics). Pin ^4.1 if you're not ready to move your framework.
  • User array shape can shift between majors. Confirm whether id, openid, and email keys moved. A silent rename breaks your firstOrCreate lookup and quietly spawns duplicate accounts. Cover the mapping with a test before deploying.
  • Redirect URI mismatch is error number one. It fails as a redirect-time error from Zalo, not a Laravel exception, so it often looks like "nothing happened." Register both http://localhost and the production URL; trailing slashes matter. The string in the console must match byte-for-byte.
  • Zalo user IDs are app-scoped. The same human gets a different id across your staging and production apps. Never merge accounts on Zalo ID alone without scoping by app, and never expose it as a public identifier (URLs, API responses).
  • Handle cancellation explicitly. Declining the consent screen throws InvalidArgumentException (Invalid state) or comes back with error in the query string. Wrap ->user() in try/catch and redirect to login with a flash message rather than surfacing a 500.
  • stateless() is for backend flows only. Calling it on the browser redirect flow disables CSRF protection on the callback. If you need it for a mobile/API client, add your own nonce verification.
  • Email may be absent or unverified depending on granted scopes. Make users.email nullable or synthesize a fallback, otherwise registration fails silently for a subset of users.
  • Local callback testing needs a public HTTPS URL — Valet share, Expose, or Cloudflare Tunnel — and the tunnel URL must be registered in the console for each change. A fixed subdomain avoids the re-registration cycle.
  • Extension points. If you need extra profile fields or a different endpoint, subclass the provider and override mapUserToObject() or getUserByToken(), then register your subclass in the SocialiteWasCalled listener instead of the shipped one. Cache the fetched profile in the session rather than calling Zalo on every request.
  • Scope discipline. This package gives you authentication plus a limited profile mapping — it is not a Zalo messaging/ZNS/OA/ZaloPay SDK. Set that expectation early, because "log in with Zalo" and "send Zalo messages" are separate integrations and stakeholders often conflate them.
  • Named owner. This is a volunteer-maintained, single-maintainer package. Assign someone in the org to watch it against Socialite releases and Zalo API changes, or you will discover breakage only when login stops working. It's a thin class and cheap to vendor or reimplement if it goes stale.
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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin