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

Wework Finance Sdk Laravel Package

mochat/wework-finance-sdk

PHP SDK for the WeCom (WeWork) Finance API, offering Laravel-friendly access to encrypted chat archive retrieval, media downloads, and signature verification. Wraps the official finance endpoints in a configurable package with a service provider for quick setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This is not a Laravel package — it is a thin PHP wrapper around Tencent’s proprietary libWeWorkFinanceSdk_C (企业微信会话内容存档). There is no service provider, no facade, and no auto-discovery. Treat it as a native-library binding you must wire yourself.

Preconditions before you write a line of code:

  1. Linux x86_64 only. Tencent ships the native .so for glibc/x86_64. There is no macOS, Windows, or ARM build. On an M1/M2 dev machine you must run inside docker run --platform linux/amd64, or you cannot even instantiate the SDK.
  2. PHP FFI enabled (extension=ffi, ffi.enable=pre or true) — most wrappers in this space load the .so via FFI. Check composer.json and src/ for whether it uses FFI or a prebuilt PHP extension; the wiring differs.
  3. The .so file itself is not in this repo. You must download the WeWorkFinanceSdk from the WeCom admin console (应用管理 → 会话内容存档) and place libWeWorkFinanceSdk_C.so somewhere writable/readable, then point config at it.
  4. The finance secret is not your corp secret. It is the dedicated secret shown in the 会话内容存档 panel, paired with the RSA public/private key you generated there. Using the app secret will fail with an unhelpful errcode.

Install and first call:

composer require mochat/wework-finance-sdk

Confirm the namespace from src/ (likely Mochat\WeWorkFinanceSdk\...). Then the minimal end-to-end smoke test:

$sdk = new FinanceSDK($corpId, $financeSecret, $privateKeyPath, $soPath);

// 1) pull a page of encrypted envelopes
$raw = $sdk->getChatData($seq = 0, $limit = 100);
$page = json_decode($raw->getData(), true);   // wrapper may return an object or string

// 2) decrypt one message
$first = $page['chatdata'][0];
$plain = $sdk->decryptData($first['encrypt_random_key'], $first['encrypt_chat_msg']);
$msg   = json_decode($plain, true);           // { msgid, action, from, tolist, msgtype, text:{content}, ... }

dump($msg['msgtype'], $msg['text']['content'] ?? null);

If that runs on your prod/staging box without a segfault, you have a working baseline. Do this on the target server, not locally.

First real use case: a scheduled job that pulls new chat records incrementally, decrypts them, and persists text messages + media pointers. Everything else is a variation on that.

Implementation Patterns

Bind the SDK as a singleton, configured from config/. You need a provider regardless of what the package ships, because you must inject paths and credentials:

// config/wework-finance.php
return [
    'corp_id'      => env('WEWORK_CORP_ID'),
    'secret'       => env('WEWORK_FINANCE_SECRET'),
    'private_key'  => storage_path('app/wework/private_key.pem'),
    'sdk_path'     => env('WEWORK_FINANCE_SO', '/usr/local/lib/libWeWorkFinanceSdk_C.so'),
    'page_size'    => 1000,
];

// AppServiceProvider::register()
$this->app->singleton(FinanceSDK::class, fn ($app) => new FinanceSDK(
    config('wework-finance.corp_id'),
    config('wework-finance.secret'),
    config('wework-finance.private_key'),
    config('wework-finance.sdk_path'),
));

Incremental pull with a persisted sequence. getChatData($seq, $limit) is exclusive of $seq — you pass the last seq you stored, and you must advance it to the max seq in the returned page, not to count(). Keep it in a dedicated table so it moves transactionally with the messages:

DB::transaction(function () use ($sdk, $cursor) {
    $page = json_decode($sdk->getChatData($cursor->seq, 1000)->getData(), true);

    foreach ($page['chatdata'] as $item) {
        $plain = json_decode($sdk->decryptData($item['encrypt_random_key'], $item['encrypt_chat_msg']), true);

        ChatMessage::updateOrCreate(
            ['msgid' => $plain['msgid']],
            ['seq' => $item['seq'], 'from' => $plain['from'], 'type' => $plain['msgtype'],
             'payload' => $plain, 'sent_at' => $plain['msgtime'] / 1000]
        );

        $cursor->seq = max($cursor->seq, $item['seq']);
    }
    $cursor->save();
});

Queue the pull, don’t inline it. Wrap the loop in a job with $timeout = 120, $tries = 3, and backoff() of [10, 60, 300]. Schedule PullFinanceMessages::dispatch() every minute via Kernel::schedule. The native call is blocking and network-bound; it will pin a worker.

Media pipeline is a second job. Messages of msgtype image/voice/video/file carry a sdkfileid, not the bytes. Decryption gives you the sdkfileid; you then call getMediaData(...) to stream it to disk. Store the sdkfileid + key in your row at decrypt time, dispatch DownloadFinanceMedia separately, and write to storage/app/wework/{yyyy}/{mm}/{msgid}.{ext}. Never do this in the pull transaction — one slow file download shouldn’t roll back a page of messages.

Normalise into a ChatMessage model with a JSON payload column rather than modelling every msgtype. WeCom adds types (meeting, docmsg, markdown, chatrecord with nested items) without notice; a JSON column absorbs that.

Sequence cursor table: finance_cursors(id, corp_id, seq, updated_at) — one row, lockForUpdate() on read. This is the single most important design decision in the whole integration.

Gotchas and Tips

  • No seq concurrency safety in the SDK. Two workers polling the same cursor will duplicate messages. updateOrCreate on msgid gives you idempotency, but you’ll still burn rate limit re-decrypting. Guard with Cache::lock('wework-finance:pull', 120).
  • The native library leaks memory and can segfault. The C API requires explicit FreeChatData / FreeDecryptChatData calls; not all PHP wrappers do this reliably. Recycle your queue workers (php artisan queue:work --max-jobs=500), and never run this in a long-lived Octane worker without --max-requests.
  • FFI misconfiguration is silent. A wrong .so path or a missing ffi.enable frequently manifests as a segfault or a null returned object rather than an exception. Check php -m | grep ffi and ldd libWeWorkFinanceSdk_C.so on the server before debugging your own code.
  • glibc mismatch. The .so is built against a specific glibc; Alpine (musl) will not load it. Use a Debian/Ubuntu base image.
  • Rate limits are real and undocumented. Tencent throttles getChatData and media downloads. Sleep ~500ms between pages, keep $limit at ≤1000, and expect errcode values like 45009 (freq limit) in the JSON body — the SDK does not throw on these; you must inspect errcode after every call.
  • Private key format. Tencent expects the RSA private key generated in the WeCom console, saved as a file path (PKCS#1). Piping a PKCS#8 key or an env-string in usually fails at decryptData.
  • msgtime is milliseconds, text.content is pre-encoded (escaped); decode and be careful with emoji/\u sequences.
  • The package is effectively unmaintained (last release Dec 2021, 7 stars, 0 dependents, non-standard license inherited from Tencent’s proprietary SDK). Do not leak it across your codebase — wrap it behind your own FinanceClient interface so that when the native .so or PHP version breaks it, you swap one class. Also verify the license terms from the WeCom console before shipping commercially; NOASSERTION here means you’re relying on Tencent’s own terms, not this repo’s.
  • Test with the console’s “试用” account first. Finance SDK requires the corp to have purchased 会话内容存档 seats; without that, getChatData returns empty with no meaningful error.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package