定制 darvis/api-linkedin 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

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

🇳🇱 Nederlandse documentatie

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
  • Knows which scopes LinkedIn actually granted, so an app without the Community Management API still connects — on the profile
  • Encrypted token storage in a linkedin_accounts table
  • Optional, ready-to-use connect/callback routes
  • LinkedIn facade 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

  1. Create an app at linkedin.com/developers/apps.
  2. Request the products Share on LinkedIn (profile) and/or Community Management API (company page).
  3. Set the redirect URL to https://your-domain.test/linkedin/callback (or your own callback route).
  4. 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).

When LinkedIn refuses the whole authorization

LinkedIn rejects the entire authorization when a single requested scope is not authorized for your app — the member never even reaches the consent screen. So an app without the Community Management API cannot connect at all as long as the config asks for company pages, however harmless that seems.

Connect with the member scopes only, and it works on any app:

<a href="{{ route('linkedin.connect', ['profile_only' => 1]) }}">Connect with my profile only</a>

Wiring the flow yourself? Pass the scopes explicitly, and hand the same set to connectFromCode() so the granted scopes are recorded truthfully even when LinkedIn leaves scope out of the token response:

use Darvis\ApiLinkedin\Scopes;

$url = LinkedIn::authorizationUrl($state, Scopes::MEMBER);
// ...
$account = LinkedIn::connectFromCode($code, Scopes::MEMBER);

Read the denial instead of parsing LinkedIn's English text (which arrives HTML-escaped, so echoing it straight into Blade shows the entities):

use Darvis\ApiLinkedin\AuthorizationDenial;

if ($denial = AuthorizationDenial::fromCallback($request)) {
    $denial->description;                    // decoded, human-readable
    $denial->isScopeProblem();               // your app lacks a product — retrying is pointless
    $denial->missingScope();                 // 'w_organization_social'
    $denial->isRecoverableWithMemberScopes();// offer the profile-only connect
}

What the connection may do

The config says what to ask for; the token says what you got. They drift apart the moment the LinkedIn app misses a product, and it is the token that decides. Gate your UI on the connection, never on the config alone — otherwise you offer targets that publish into a 403:

LinkedIn::canListOrganizations();   // config allows it AND the token carries r_organization_admin
LinkedIn::canPostAsOrganization();  // the token carries w_organization_social

$account = LinkedIn::account();
$account->grantedScopes();          // ['openid', 'profile', 'w_member_social'] — or null when unknown
$account->hasScope(Scopes::POST_AS_ORGANIZATION);

A connection stored before 1.4 has no recorded scopes. grantedScopes() is then null and both hasScope() and lacksScope() return false: the package refuses to guess in either direction, so nothing is offered that it cannot promise and nothing is blocked that might still work.

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_admin scope, 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, and organizations() throws LinkedInScopeMissing until you do. The list is cached for linkedin.organizations.cache_ttl seconds (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,
    LinkedInScopeMissing,
};

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 (LinkedInScopeMissing $e) {
    // $e->scope was never granted. Add the product to the LinkedIn app and reconnect;
    // no request was sent, because LinkedIn would only have answered 403.
} 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
LinkedInScopeMissing The token provably lacks the scope this call needs (scope); thrown before any request goes out
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
  • 点击次数: 2
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-07-13

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固