darvis/api-linkedin
Composer 安装命令:
composer require darvis/api-linkedin
包简介
Universal Laravel integration for publishing posts on LinkedIn (personal profile and company page) through OAuth 2.0 and the Posts API.
README 文档
README
Universal Laravel integration for publishing posts on LinkedIn — both on your personal profile and on a company page — through OAuth 2.0 and the LinkedIn Posts API. Without external dependencies (only the built-in HTTP client).
- OAuth 2.0 authorization code flow with automatic token refresh
- Post on behalf of a member (
w_member_social) or an organization (w_organization_social) - List the company pages you administer and pick one per post
- Encrypted token storage in a
linkedin_accountstable - Optional, ready-to-use connect/callback routes
LinkedInfacade for a one-liner post
Installation
composer require darvis/api-linkedin
Optionally publish the config and the migration (the migration is loaded automatically as well):
php artisan vendor:publish --tag=linkedin-config php artisan vendor:publish --tag=linkedin-migrations php artisan migrate
Setting up the LinkedIn app
- Create an app at linkedin.com/developers/apps.
- Request the products Share on LinkedIn (profile) and/or Community Management API (company page).
- Set the redirect URL to
https://your-domain.test/linkedin/callback(or your own callback route). - Fill in your
.env:
LINKEDIN_CLIENT_ID=... LINKEDIN_CLIENT_SECRET=... # Company page — leave empty to post on your profile only LINKEDIN_ORGANIZATION_URN=urn:li:organization:1234567 # Valid, recent API version (YYYYMM) LINKEDIN_API_VERSION=202601
Connecting
Send the user to the built-in connect route:
<a href="{{ route('linkedin.connect') }}">Connect with LinkedIn</a>
Afterwards the user is redirected back (see linkedin.routes.redirect_to) with a
flash message in session('linkedin_status') or session('linkedin_error').
Want to wire the flow yourself? Set linkedin.routes.enabled to false and use
LinkedIn::authorizationUrl($state) and LinkedIn::connectFromCode($code).
Publishing
use Darvis\ApiLinkedin\Facades\LinkedIn; // On your personal profile LinkedIn::postAsMember("New blog article!\n\nhttps://example.com/blog/my-article"); // On the default company page (linkedin.organization_urn) LinkedIn::postAsOrganization('Company news with a link https://example.com'); // On a specific company page LinkedIn::postAsOrganization('Company news', 'urn:li:organization:1234567');
All of these return ['urn' => '...', 'permalink' => '...']. Put a URL in the text
and LinkedIn builds the link preview itself from the Open Graph tags of that page.
Tip: run the publishing in a queued job, so a slow or failing API call does not block your request.
Listing your company pages
Do you administer several company pages and want the user to pick one? Turn the listing on and ask LinkedIn which pages the connected member administers:
LINKEDIN_ORGANIZATIONS_ENABLED=true
LinkedIn::organizations(); // [ // ['urn' => 'urn:li:organization:42', 'id' => '42', 'name' => 'Acme BV', 'vanity_name' => 'acme'], // ['urn' => 'urn:li:organization:99', 'id' => '99', 'name' => 'Acme Labs', 'vanity_name' => 'acme-labs'], // ] LinkedIn::organizations(fresh: true); // bypass the cache LinkedIn::forgetOrganizations(); // drop the cached list
Combine it with postAsOrganization($text, $urn) to let a user choose a target
per post.
Two things to know. Listing requires the
r_organization_adminscope, which is only requested when this setting is on and requires Community Management API access. Turning it on after connecting means your existing token does not carry the scope — you have to reconnect. The list is cached forlinkedin.organizations.cache_ttlseconds (one hour by default).
Through the services (dependency injection)
use Darvis\ApiLinkedin\Models\LinkedInAccount; use Darvis\ApiLinkedin\Services\LinkedInPublisher; public function share(LinkedInPublisher $publisher) { $account = LinkedInAccount::current(); $publisher->publish($account, $account->member_urn, 'Text with a link https://example.com'); }
Error handling
Everything throws a LinkedInException, but each failure has its own type — react
on the type, not on the message text (that is free to change between releases).
use Darvis\ApiLinkedin\Exceptions\{ LinkedInApiException, LinkedInConnectionExpired, LinkedInNotConnected, }; try { LinkedIn::postAsMember($text); } catch (LinkedInNotConnected) { // Nobody connected an account yet. } catch (LinkedInConnectionExpired) { // Send the user back through the OAuth flow — the only failure they can fix. } catch (LinkedInApiException $e) { // $e->operation 'token' | 'profile' | 'publish' | 'organizations' // $e->status HTTP status // $e->body raw response body if ($e->isAuthorizationProblem()) { // 401/403: scope or permission issue // ... } }
| Exception | Meaning |
|---|---|
LinkedInNotConnected |
No account connected |
LinkedInConnectionExpired |
Token expired and not refreshable — reconnect |
LinkedInConfigurationException |
A required setting is missing |
LinkedInApiException |
LinkedIn returned an error (operation, status, body) |
All of them extend LinkedInException, so a single catch (LinkedInException $e)
still catches everything.
Configuration
All keys live in config/linkedin.php. The important ones:
| Key | Description |
|---|---|
organization_urn |
Default company page URN; empty = profile only |
organizations.enabled |
Allow LinkedIn::organizations() to list your pages (adds r_organization_admin) |
organizations.cache_ttl |
Seconds to cache that list; 0 = no cache |
api_version |
LinkedIn API version (YYYYMM) |
routes.enabled |
Turn the built-in connect/callback routes on/off |
routes.prefix / routes.middleware |
Prefix and middleware of those routes |
routes.callback_name |
Route name used for the redirect_uri (when using your own routes) |
routes.redirect_to |
Route name to redirect back to after the flow |
table |
Name of the accounts table |
Testing
composer install
composer test
License
MIT © Arvid de Jong
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-13