木子屋 Dnawo's BLOG

企业微信自建应用发消息:实现 API 推送消息到微信

👤 dnawo 📅 2026-10-14 👁 28 👍 0 💬 0 🔄 本站原创 📂 其他
以前尝试用ntfy在手机接收自定义消息,但ntfy常常被关闭,最近发现调用企业微信API可以给微信发送文本消息,实现原理是微信加入企业后,在企业微信创建应用,然后调用API让应用给企业成员发送消息,成员在微信查看消息,这样曲线实现将自定义消息发送到微信上,下面分享一下方法。

一、企业微信应用创建和配置

1.1 创建应用,获取AgentId和Secret

登录企业微信(链接),创建应用,记下AgentId和Secret(只能在企业微信APP查看):

图片

图片

1.2 获取CorpID(企业ID)

打开我的企业>企业信息,在页面下方可以看到企业ID。

1.2 配置可信IP

本地或服务器IP必须添加到可信IP列表才能成功调用API,但是配置可信IP前,要求先设置可信域名,在开发者接口设置可信域名:

图片

图片

然后配置企业可信IP:

图片

图片

二、微信关注微信插件

微信关注微信插件后,无需下载企业微信客户端,即可在微信中接收企业通知和使用企业应用。打开我的企业>微信插件,微信扫描页面上的二维码并关注,并确保取消钩选"成员使用微信插件时需要使用企业微信客户端"和钩选"允许成员在微信插件中接收和回复聊天消息":

图片

三、调用API给微信发送自定义消息

打开我的企业>企业信息,获取CorpID(企业ID);打开通讯录>组织架构,获取成员账号;和上面获取到的AgentId、Secret一起填入下面程序即可发送消息:

<?php
declare(strict_types=1);

// Web 访问时输出纯文本(CLI 下不调用 header)
if (PHP_SAPI !== 'cli') {
    header('Content-Type: text/plain; charset=utf-8');
}

// ============================ 配置区:请替换为你的真实值 ============================
const CORP_ID     = 'CorpID';     // 企业 ID:企业微信管理后台「我的企业 → 企业信息」中查看
const CORP_SECRET = 'Secret';  // 应用 Secret:应用管理 → 自建应用 → 详情 → 「Secret」查看/重置
const AGENT_ID    = AgentId;                  // 应用 AgentId(整型):同一应用详情页可查看
const TO_USER     = '成员账号';                    // 接收人:企业微信通讯录中的 userid(账号),不是姓名/备注
const MSG_CONTENT = '自定义消息';           // 消息内容
// =====================================================================================

// ---------- 1. 获取 access_token ----------
// GET https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
$token = getAccessToken();
if ($token === null) {
    exit(1);
}
echo "获取 access_token 成功\n";

// ---------- 2. 发送文本消息 ----------
// POST https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN
[$result, $err] = sendTextMessage($token, TO_USER, AGENT_ID, MSG_CONTENT);

if ($err !== null) {
    echo '[message/send] ' . $err . "\n";
    exit(1);
}

if (($result['errcode'] ?? -1) === 0) {
    echo '消息发送成功,errmsg: ' . ($result['errmsg'] ?? 'ok') . "\n";
    if (!empty($result['msgid'])) {
        echo 'msgid: ' . $result['msgid'] . "\n";
    }
    exit(0);
}

echo sprintf(
    "[message/send] errcode=%d errmsg=%s\n",
    $result['errcode'] ?? -1,
    $result['errmsg'] ?? ''
);
// 常见失败原因排查提示
if (!empty($result['invaliduser'])) {
    echo 'invaliduser: ' . $result['invaliduser']
        . " —— 该 userid 不存在或不在应用可见范围内\n";
}
if (!empty($result['unlicenseduser'])) {
    echo 'unlicenseduser: ' . $result['unlicenseduser']
        . " —— 该成员在应用可见范围内但缺少基础接口许可\n";
}
exit(1);

/**
 * 获取 access_token
 */
function getAccessToken(): ?string
{
    $url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken'
        . '?corpid=' . urlencode(CORP_ID)
        . '&corpsecret=' . urlencode(CORP_SECRET);

    [$data, $err] = httpRequest($url);
    if ($err !== null) {
        echo '[gettoken] ' . $err . "\n";
        return null;
    }
    if (($data['errcode'] ?? 1) !== 0) {
        echo sprintf(
            "[gettoken] errcode=%d errmsg=%s\n",
            $data['errcode'] ?? -1,
            $data['errmsg'] ?? ''
        );
        return null;
    }
    return $data['access_token'] ?? null;
}

/**
 * 发送文本消息
 *
 * @return array{0: array|null, 1: string|null} [接口返回的 JSON 数组, 请求错误信息]
 */
function sendTextMessage(string $token, string $toUser, int $agentId, string $content): array
{
    $url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' . urlencode($token);

    $payload = [
        'touser'  => $toUser,
        'msgtype' => 'text',
        'agentid' => $agentId,
        'text'    => ['content' => $content],
        'safe'    => 0,
    ];

    return httpRequest($url, 'POST', json_encode($payload, JSON_UNESCAPED_UNICODE));
}

/**
 * 基于 cURL 的 HTTP 请求封装
 *
 * 说明:PHP 8.0+ 的 curl 句柄由引擎自动释放,无需(也不应)调用 curl_close()。
 *
 * @return array{0: array|null, 1: string|null} [解码后的 JSON 数组, 错误信息]
 */
function httpRequest(string $url, string $method = 'GET', ?string $body = null): array
{
    $ch = curl_init($url);

    $options = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
    ];

    // 仅当 cacert.pem 存在且为有效 PEM 文本时才指定 CAINFO;
    // 文件缺失/损坏时跳过,回退到 php.ini 的 curl.cainfo 配置,避免直接中断
    $caInfo = __DIR__ . '/cacert.pem';
    if (is_file($caInfo) && filesize($caInfo) > 0
        && strpos((string)file_get_contents($caInfo, false, null, 0, 1024), '-----BEGIN CERTIFICATE-----') !== false
    ) {
        $options[CURLOPT_CAINFO] = $caInfo;
    } else {
        echo '[提示] cacert.pem 缺失或无效,未指定 CAINFO,将依赖 php.ini 的 curl.cainfo;'
            . "若仍报证书错误,请重新下载有效的 cacert.pem\n";
    }

    curl_setopt_array($ch, $options);

    if ($method === 'POST') {
        curl_setopt($ch, CURLOPT_POST, true);
        if ($body !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json; charset=utf-8']);
        }
    }

    $resp = curl_exec($ch);
    if ($resp === false) {
        return [null, 'curl error: ' . curl_error($ch)];
    }
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

    $data = json_decode($resp, true);
    if (!is_array($data)) {
        return [null, '响应不是合法 JSON(HTTP ' . $httpCode . '): ' . substr($resp, 0, 200)];
    }
    return [$data, null];
}

最终效果:

图片

相关链接

[1].企业微信:https://work.weixin.qq.com/
[2].发送应用消息:https://developer.work.weixin.qq.com/document/path/90236
[3].源码下载:https://www.mzwu.com/work_weixin_messages.rar

评论(0)

暂无评论。

计算题
评论需审核通过后显示
← 上一篇 对称加密 (AES) 与非对称加密 (RSA) 完整示例