├── docs
├── .nojekyll
├── thinkphp.md
├── crm.md
├── corp.md
├── token.md
├── auth.md
├── agent.md
├── checkin.md
├── jssdk.md
├── laravel.md
├── media.md
├── menu.md
├── department.md
├── batch.md
├── invoice.md
├── tag.md
├── chat.md
├── _sidebar.md
├── quick-start.md
├── README.md
├── user.md
├── index.html
├── callback.md
└── message.md
├── examples
├── public
│ ├── wework.txt
│ └── 20180103195745.png
├── cache
│ └── .gitignore
├── log
│ └── .gitignore
├── crm.php
├── corp.php
├── token.php
├── ticket.php
├── jssdk.php
├── jsticket.php
├── agent.php
├── checkin.php
├── menu.php
├── app.php
├── media.php
├── batch.php
├── invoice.php
├── department.php
├── tag.php
├── callback.php
├── user.php
├── message.php
└── chat.php
├── .gitignore
├── .travis.yml
├── src
├── Message
│ ├── ReplyMessageInterface.php
│ ├── ResponseMessageInterface.php
│ ├── File.php
│ ├── Markdown.php
│ ├── MPNews.php
│ ├── Text.php
│ ├── Image.php
│ ├── Voice.php
│ ├── News.php
│ ├── Receiver.php
│ ├── TextCard.php
│ ├── Video.php
│ ├── MPArticle.php
│ └── Article.php
├── Laravel
│ ├── LogBridge.php
│ ├── CacheBridge.php
│ ├── config.php
│ └── WeWorkServiceProvider.php
├── Traits
│ ├── CorpIdTrait.php
│ ├── AgentIdTrait.php
│ ├── SecretTrait.php
│ ├── TicketTrait.php
│ ├── CacheTrait.php
│ ├── JsApiTicketTrait.php
│ └── HttpClientTrait.php
├── Api
│ ├── CRM.php
│ ├── Corp.php
│ ├── Message.php
│ ├── Agent.php
│ ├── Menu.php
│ ├── Department.php
│ ├── CheckIn.php
│ ├── Media.php
│ ├── Batch.php
│ ├── AppChat.php
│ ├── Invoice.php
│ ├── Tag.php
│ └── User.php
├── ApiCache
│ ├── Ticket.php
│ ├── JsApiTicket.php
│ ├── Token.php
│ └── AbstractApiCache.php
├── Http
│ ├── Response.php
│ ├── ClientFactory.php
│ ├── HttpClientInterface.php
│ ├── HttpClient.php
│ └── Middleware.php
├── Crypt
│ ├── SHA1.php
│ ├── ErrorCode.php
│ ├── PKCS7Encoder.php
│ ├── XMLParse.php
│ ├── PrpCrypt.php
│ └── WXBizMsgCrypt.php
├── JSSdk.php
├── Callback.php
└── App.php
├── .editorconfig
├── tests
├── Message
│ ├── FileTest.php
│ ├── MarkdownTest.php
│ ├── TextCardTest.php
│ ├── MPArticleTest.php
│ ├── TextTest.php
│ ├── ImageTest.php
│ ├── VoiceTest.php
│ ├── ArticleTest.php
│ ├── MPNewsTest.php
│ ├── VideoTest.php
│ ├── NewsTest.php
│ └── ReceiverTest.php
├── Http
│ ├── ClientFactoryTest.php
│ ├── ResponseTest.php
│ ├── MiddlewareTest.php
│ └── HttpClientTest.php
├── TestCase.php
├── Api
│ ├── CRMTest.php
│ ├── CorpTest.php
│ ├── MessageTest.php
│ ├── AgentTest.php
│ ├── MenuTest.php
│ ├── MediaTest.php
│ ├── InvoiceTest.php
│ ├── AppChatTest.php
│ ├── DepartmentTest.php
│ ├── BatchTest.php
│ ├── CheckInTest.php
│ ├── TagTest.php
│ └── UserTest.php
├── ApiCache
│ ├── TicketTest.php
│ ├── JsTicketTest.php
│ ├── TokenTest.php
│ └── AbstractApiCacheTest.php
├── AppTest.php
├── JSSdkTest.php
├── CallbackTest.php
└── Crypt
│ └── WXBizMsgCryptTest.php
├── phpunit.xml.dist
├── README.md
├── LICENSE
└── composer.json
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/public/wework.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/public/20180103195745.png:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/cache/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/examples/log/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /vendor/
3 | /composer.lock
4 |
--------------------------------------------------------------------------------
/docs/thinkphp.md:
--------------------------------------------------------------------------------
1 | # ThinkPHP
2 |
3 | 请参考 [pithyone/think-wechat](https://github.com/pithyone/think-wechat)
4 |
--------------------------------------------------------------------------------
/docs/crm.md:
--------------------------------------------------------------------------------
1 | # 外部联系人管理
2 |
3 | ```php
4 | $crm = $app->get('crm');
5 | ```
6 |
7 | ## 获取外部联系人详情
8 |
9 | ```php
10 | $crm->getExternalContact('EXTERNAL_USERID');
11 | ```
12 |
--------------------------------------------------------------------------------
/docs/corp.md:
--------------------------------------------------------------------------------
1 | # 审批管理
2 |
3 | ```php
4 | $corp = $app->get('corp');
5 | ```
6 |
7 | ## 获取审批数据
8 |
9 | ```php
10 | $corp->getApprovalData(1492617600, 1492790400, 201704200003);
11 | ```
12 |
--------------------------------------------------------------------------------
/docs/token.md:
--------------------------------------------------------------------------------
1 | # access_token
2 |
3 | ```php
4 | $token = $app->get('token');
5 | ```
6 |
7 | ## 获取
8 |
9 | ```php
10 | $token->get();
11 | ```
12 |
13 | ## 刷新
14 |
15 | ```php
16 | $token->get(true);
17 | ```
18 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 7.1
5 | - 7.2
6 |
7 | before_install: composer self-update
8 |
9 | install: composer install --prefer-source --no-interaction --dev
10 |
11 | script: ./vendor/bin/phpunit
12 |
--------------------------------------------------------------------------------
/src/Message/ReplyMessageInterface.php:
--------------------------------------------------------------------------------
1 | get('crm');
7 |
8 | try {
9 | $crm->getExternalContact('EXTERNAL_USERID');
10 | } catch (Exception $e) {
11 | }
12 |
--------------------------------------------------------------------------------
/src/Message/ResponseMessageInterface.php:
--------------------------------------------------------------------------------
1 | get('user');
5 | ```
6 |
7 | ## 根据code获取成员信息
8 |
9 | ```php
10 | $user->getInfo('CODE');
11 | ```
12 |
13 | ## 使用user_ticket获取成员详情
14 |
15 | ```php
16 | $user->getDetail('USER_TICKET');
17 | ```
18 |
--------------------------------------------------------------------------------
/examples/corp.php:
--------------------------------------------------------------------------------
1 | get('corp');
7 |
8 | try {
9 | $corp->getApprovalData(1492617600, 1492790400, 201704200003);
10 | } catch (Exception $e) {
11 | }
12 |
--------------------------------------------------------------------------------
/docs/agent.md:
--------------------------------------------------------------------------------
1 | # 应用管理
2 |
3 | ```php
4 | $agent = $app->get('agent');
5 | ```
6 |
7 | ## 获取应用
8 |
9 | ```php
10 | $agent->get();
11 | ```
12 |
13 | ## 设置应用
14 |
15 | ```php
16 | $agent->set([
17 | 'close' => 0
18 | ]);
19 | ```
20 |
21 | ## 获取应用列表
22 |
23 | ```php
24 | $agent->list();
25 | ```
26 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | insert_final_newline = true
7 | indent_style = space
8 | indent_size = 4
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
14 | [*.yml]
15 | indent_style = space
16 | indent_size = 2
--------------------------------------------------------------------------------
/docs/checkin.md:
--------------------------------------------------------------------------------
1 | # 打卡管理
2 |
3 | ```php
4 | $checkIn = $app->get('checkIn');
5 | ```
6 |
7 | ## 获取打卡规则
8 |
9 | ```php
10 | $checkIn->getOption(1511971200, ['james', 'paul']);
11 | ```
12 |
13 | ## 获取打卡数据
14 |
15 | ```php
16 | $checkIn->getData(\WeWork\Api\CheckIn::TYPE_ALL, 1492617600, 1492790400, ['james', 'paul']);
17 | ```
18 |
--------------------------------------------------------------------------------
/src/Laravel/LogBridge.php:
--------------------------------------------------------------------------------
1 | corpId = $corpId;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Traits/AgentIdTrait.php:
--------------------------------------------------------------------------------
1 | agentId = $agentId;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Traits/SecretTrait.php:
--------------------------------------------------------------------------------
1 | secret = $secret;
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/examples/token.php:
--------------------------------------------------------------------------------
1 | get('token');
7 |
8 | try {
9 | $token->get();
10 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
11 | }
12 |
13 | try {
14 | $token->get(true);
15 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
16 | }
17 |
--------------------------------------------------------------------------------
/examples/ticket.php:
--------------------------------------------------------------------------------
1 | get('ticket');
7 |
8 | try {
9 | $ticket->get();
10 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
11 | }
12 |
13 | try {
14 | $ticket->get(true);
15 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
16 | }
17 |
--------------------------------------------------------------------------------
/docs/jssdk.md:
--------------------------------------------------------------------------------
1 | # JS-SDK
2 |
3 | ## 获取企业的jsapi_ticket
4 |
5 | ```php
6 | $app->get('jsApiTicket')->get();
7 | ```
8 |
9 | ## 生成权限验证配置
10 |
11 | ```php
12 | $app->get('jssdk')->getConfig('URL');
13 | ```
14 |
15 | ## 获取电子发票ticket
16 |
17 | ```php
18 | $app->get('ticket')->get();
19 | ```
20 |
21 | ## 生成拉起电子发票列表配置
22 |
23 | ```php
24 | $app->get('jssdk')->getChooseInvoiceConfig();
25 | ```
26 |
--------------------------------------------------------------------------------
/src/Traits/TicketTrait.php:
--------------------------------------------------------------------------------
1 | ticket = $ticket;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/laravel.md:
--------------------------------------------------------------------------------
1 | # Laravel
2 |
3 | ## 创建配置文件
4 |
5 | ``` bash
6 | php artisan vendor:publish --provider="WeWork\Laravel\WeWorkServiceProvider"
7 | ```
8 |
9 | ## 用法
10 |
11 | 以获取 access_token 为例:
12 |
13 | ```php
14 | app('wework')->get('token')->get();
15 | ```
16 |
17 | 使用其他应用时,改变默认设置即可,假设有一个测试应用为 `test`
18 |
19 | ```php
20 | app()->make('wework', ['default' => 'test'])->get('token')->get();
21 | ```
22 |
--------------------------------------------------------------------------------
/examples/jssdk.php:
--------------------------------------------------------------------------------
1 | get('jssdk');
7 |
8 | try {
9 | var_dump($jssdk->getConfig('URL'));
10 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
11 | }
12 |
13 | try {
14 | var_dump($jssdk->getChooseInvoiceConfig());
15 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
16 | }
17 |
--------------------------------------------------------------------------------
/examples/jsticket.php:
--------------------------------------------------------------------------------
1 | get('jsApiTicket');
7 |
8 | try {
9 | $jsApiTicket->get();
10 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
11 | }
12 |
13 | try {
14 | $jsApiTicket->get(true);
15 | } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
16 | }
17 |
--------------------------------------------------------------------------------
/examples/agent.php:
--------------------------------------------------------------------------------
1 | get('agent');
7 |
8 | try {
9 | $agent->get();
10 | } catch (Exception $e) {
11 | }
12 |
13 | try {
14 | $agent->set([
15 | 'close' => 0
16 | ]);
17 | } catch (Exception $e) {
18 | }
19 |
20 | try {
21 | $agent->list();
22 | } catch (Exception $e) {
23 | }
24 |
--------------------------------------------------------------------------------
/examples/checkin.php:
--------------------------------------------------------------------------------
1 | get('checkIn');
7 |
8 | try {
9 | $checkIn->getOption(1511971200, ['james', 'paul']);
10 | } catch (Exception $e) {
11 | }
12 |
13 | try {
14 | $checkIn->getData(\WeWork\Api\CheckIn::TYPE_ALL, 1492617600, 1492790400, ['james', 'paul']);
15 | } catch (Exception $e) {
16 | }
17 |
--------------------------------------------------------------------------------
/src/Traits/CacheTrait.php:
--------------------------------------------------------------------------------
1 | cache = $cache;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/media.md:
--------------------------------------------------------------------------------
1 | # 素材管理
2 |
3 | ```php
4 | $media = $app->get('media');
5 | ```
6 |
7 | ## 上传临时素材
8 |
9 | ```php
10 | $media->upload('file', __DIR__ . '/public/wework.txt');
11 | ```
12 |
13 | ## 获取临时素材
14 |
15 | ```php
16 | $media->get('MEDIA_ID');
17 | ```
18 |
19 | ## 获取高清语音素材
20 |
21 | ```php
22 | $media->getVoice('MEDIA_ID');
23 | ```
24 |
25 | ## 上传图片
26 |
27 | ```php
28 | $media->uploadImg(__DIR__ . '/public/20180103195745.png');
29 | ```
30 |
--------------------------------------------------------------------------------
/docs/menu.md:
--------------------------------------------------------------------------------
1 | # 自定义菜单
2 |
3 | ```php
4 | $menu = $app->get('menu');
5 | ```
6 |
7 | ## 创建菜单
8 |
9 | ```php
10 | $menu->create([
11 | 'button' => [
12 | [
13 | 'type' => 'click',
14 | 'name' => '今日歌曲',
15 | 'key' => 'V1001_TODAY_MUSIC'
16 | ]
17 | ]
18 | ]);
19 | ```
20 |
21 | ## 获取菜单
22 |
23 | ```php
24 | $menu->get();
25 | ```
26 |
27 | ## 删除菜单
28 |
29 | ```php
30 | $menu->delete();
31 | ```
32 |
--------------------------------------------------------------------------------
/src/Traits/JsApiTicketTrait.php:
--------------------------------------------------------------------------------
1 | jsApiTicket = $jsApiTicket;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Traits/HttpClientTrait.php:
--------------------------------------------------------------------------------
1 | httpClient = $httpClient;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Api/CRM.php:
--------------------------------------------------------------------------------
1 | httpClient->get('crm/get_external_contact', ['external_userid' => $externalUserId]);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/department.md:
--------------------------------------------------------------------------------
1 | # 部门管理
2 |
3 | ```php
4 | $department = $app->get('department');
5 | ```
6 |
7 | ## 创建部门
8 |
9 | ```php
10 | $department->create([
11 | 'name' => '广州研发中心',
12 | 'parentid' => 1,
13 | 'order' => 1,
14 | 'id' => 1024
15 | ]);
16 | ```
17 |
18 | ## 更新部门
19 |
20 | ```php
21 | $department->update([
22 | 'id' => 1024,
23 | 'name' => '广州研发中心',
24 | ]);
25 | ```
26 |
27 | ## 删除部门
28 |
29 | ```php
30 | $department->delete(1024);
31 | ```
32 |
33 | ## 获取部门列表
34 |
35 | ```php
36 | $department->list();
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/batch.md:
--------------------------------------------------------------------------------
1 | # 异步批量接口
2 |
3 | ```php
4 | $batch = $app->get('batch');
5 | ```
6 |
7 | ## 邀请成员
8 |
9 | ```php
10 | $batch->invite([
11 | 'user' => ['UserID1', 'UserID2', 'UserID3']
12 | ]);
13 | ```
14 |
15 | ## 增量更新成员
16 |
17 | ```php
18 | $batch->syncUser(['media_id' => 'xxxxxx']);
19 | ```
20 |
21 | ## 全量覆盖成员
22 |
23 | ```php
24 | $batch->replaceUser(['media_id' => 'xxxxxx']);
25 | ```
26 |
27 | ## 全量覆盖部门
28 |
29 | ```php
30 | $batch->replaceParty(['media_id' => 'xxxxxx']);
31 | ```
32 |
33 | ## 获取异步任务结果
34 |
35 | ```php
36 | $batch->getResult('JOBID');
37 | ```
38 |
--------------------------------------------------------------------------------
/examples/menu.php:
--------------------------------------------------------------------------------
1 | get('menu');
7 |
8 | try {
9 | $menu->create([
10 | 'button' => [
11 | [
12 | 'type' => 'click',
13 | 'name' => '今日歌曲',
14 | 'key' => 'V1001_TODAY_MUSIC'
15 | ]
16 | ]
17 | ]);
18 | } catch (Exception $e) {
19 | }
20 |
21 | try {
22 | $menu->get();
23 | } catch (Exception $e) {
24 | }
25 |
26 | try {
27 | $menu->delete();
28 | } catch (Exception $e) {
29 | }
30 |
--------------------------------------------------------------------------------
/src/ApiCache/Ticket.php:
--------------------------------------------------------------------------------
1 | httpClient->get('ticket/get', ['type' => 'wx_card']);
25 |
26 | return $data['ticket'];
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/docs/invoice.md:
--------------------------------------------------------------------------------
1 | # 电子发票
2 |
3 | ```php
4 | $invoice = $app->get('invoice');
5 | ```
6 |
7 | ## 查询电子发票
8 |
9 | ```php
10 | $invoice->getInfo('CARDID', 'ENCRYPTCODE');
11 | ```
12 |
13 | ## 更新发票状态
14 |
15 | ```php
16 | $invoice->updateStatus('CARDID', 'ENCRYPTCODE', 'INVOICE_REIMBURSE_INIT');
17 | ```
18 |
19 | ## 批量更新发票状态
20 |
21 | ```php
22 | $invoice->updateStatusBatch('OPENID', 'INVOICE_REIMBURSE_INIT', [
23 | ['card_id' => 'CARDID', 'encrypt_code' => 'ENCRYPTCODE']
24 | ]);
25 | ```
26 |
27 | ## 批量查询电子发票
28 |
29 | ```php
30 | $invoice->getInfoBatch([
31 | ['card_id' => 'CARDID', 'encrypt_code' => 'ENCRYPTCODE']
32 | ]);
33 | ```
34 |
--------------------------------------------------------------------------------
/examples/app.php:
--------------------------------------------------------------------------------
1 | 'xxxxxxxxxxxxxxxxxx',
8 | 'secret' => 'fsAQmTHCGvd5ZsRJnkr9jj9aCR2sqOKypAUw6D3Jy5Y',
9 | 'agent_id' => 0,
10 | 'token' => 'cxvhat5gfJW30J9YUsq',
11 | 'aes_key' => 'ULPUgqKDC8LTI1fRJ1jOxHrwbUV2QxR5jnSKrlh4meT',
12 | 'cache' => [
13 | 'path' => __DIR__.'/cache',
14 | ],
15 | 'log' => [
16 | 'file' => __DIR__.'/log/app.log',
17 | 'level' => 'debug',
18 | ],
19 | ]
20 | );
21 |
22 | return $app;
23 |
--------------------------------------------------------------------------------
/docs/tag.md:
--------------------------------------------------------------------------------
1 | # 标签管理
2 |
3 | ```php
4 | $tag = $app->get('tag');
5 | ```
6 |
7 | ## 创建标签
8 |
9 | ```php
10 | $tag->create('UI', 1024);
11 | ```
12 |
13 | ## 更新标签名字
14 |
15 | ```php
16 | $tag->update(1024, 'UI design');
17 | ```
18 |
19 | ## 删除标签
20 |
21 | ```php
22 | $tag->delete(1024);
23 | ```
24 |
25 | ## 获取标签成员
26 |
27 | ```php
28 | $tag->get(1024);
29 | ```
30 |
31 | ## 增加标签成员
32 |
33 | ```php
34 | $tag->addUsers(['tagid' => 1024, 'userlist' => ['user1', 'user2']]);
35 | ```
36 |
37 | ## 删除标签成员
38 |
39 | ```php
40 | $tag->delUsers(['tagid' => 1024, 'userlist' => ['user1', 'user2']]);
41 | ```
42 |
43 | ## 获取标签列表
44 |
45 | ```php
46 | $tag->list();
47 | ```
48 |
--------------------------------------------------------------------------------
/src/Message/File.php:
--------------------------------------------------------------------------------
1 | mediaId = $mediaId;
18 | }
19 |
20 | /**
21 | * @return array
22 | */
23 | public function formatForResponse(): array
24 | {
25 | return [
26 | 'msgtype' => 'file',
27 | 'file' => [
28 | 'media_id' => $this->mediaId
29 | ]
30 | ];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/media.php:
--------------------------------------------------------------------------------
1 | get('media');
7 |
8 | try {
9 | $media->upload('file', __DIR__ . '/public/wework.txt');
10 | } catch (Exception $e) {
11 | }
12 |
13 | try {
14 | $data = $media->get('MEDIA_ID');
15 | file_put_contents(__DIR__ . '/app/MEDIA_ID.jpg', $data);
16 | } catch (Exception $e) {
17 | }
18 |
19 | try {
20 | $data = $media->getVoice('MEDIA_ID');
21 | file_put_contents(__DIR__ . '/app/XXX', $data);
22 | } catch (Exception $e) {
23 | }
24 |
25 | try {
26 | $media->uploadImg(__DIR__ . '/public/20180103195745.png');
27 | } catch (Exception $e) {
28 | }
29 |
--------------------------------------------------------------------------------
/examples/batch.php:
--------------------------------------------------------------------------------
1 | get('batch');
7 |
8 | try {
9 | $batch->invite([
10 | 'user' => ['UserID1', 'UserID2', 'UserID3']
11 | ]);
12 | } catch (Exception $e) {
13 | }
14 |
15 | try {
16 | $batch->syncUser(['media_id' => 'xxxxxx']);
17 | } catch (Exception $e) {
18 | }
19 |
20 | try {
21 | $batch->replaceUser(['media_id' => 'xxxxxx']);
22 | } catch (Exception $e) {
23 | }
24 |
25 | try {
26 | $batch->replaceParty(['media_id' => 'xxxxxx']);
27 | } catch (Exception $e) {
28 | }
29 |
30 | try {
31 | $batch->getResult('JOBID');
32 | } catch (Exception $e) {
33 | }
34 |
--------------------------------------------------------------------------------
/src/ApiCache/JsApiTicket.php:
--------------------------------------------------------------------------------
1 | secret);
18 |
19 | return md5('wework.api.js_ticket.' . $unique);
20 | }
21 |
22 | /**
23 | * @return string
24 | */
25 | protected function getFromServer(): string
26 | {
27 | $data = $this->httpClient->get('get_jsapi_ticket');
28 |
29 | return $data['ticket'];
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/docs/chat.md:
--------------------------------------------------------------------------------
1 | # 发送消息到群聊会话
2 |
3 | ```php
4 | $chat = $app->get('appChat');
5 | ```
6 |
7 | ## 创建群聊会话
8 |
9 | ```php
10 | $chat->create([
11 | 'name' => 'NAME',
12 | 'owner' => 'userid1',
13 | 'userlist' => ['userid1', 'userid2'],
14 | 'chatid' => 'CHATID'
15 | ]);
16 | ```
17 |
18 | ## 修改群聊会话
19 |
20 | ```php
21 | $chat->update([
22 | 'chatid' => 'CHATID',
23 | 'name' => 'NAME',
24 | 'owner' => 'userid2'
25 | ]);
26 | ```
27 |
28 | ## 获取群聊会话
29 |
30 | ```php
31 | $chat->get('CHATID');
32 | ```
33 |
34 | ## 应用推送消息
35 |
36 | ```php
37 | $text = new \WeWork\Message\Text("你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看邮件中心视频实况,聪明避开排队。");
38 | $chat->send('CHATID', $text, true);
39 | ```
40 |
--------------------------------------------------------------------------------
/src/Message/Markdown.php:
--------------------------------------------------------------------------------
1 | content = $content;
20 | }
21 |
22 | /**
23 | * @inheritDoc
24 | */
25 | public function formatForResponse(): array
26 | {
27 | return [
28 | 'msgtype' => 'markdown',
29 | 'markdown' => [
30 | 'content' => $this->content,
31 | ]
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | * 入门
2 |
3 | * [快速开始](quick-start.md)
4 | * [access_token](token.md)
5 |
6 | * 通讯录管理
7 |
8 | * [成员管理](user.md)
9 | * [部门管理](department.md)
10 | * [标签管理](tag.md)
11 | * [异步批量接口](batch.md)
12 |
13 | * [外部联系人管理](crm.md)
14 |
15 | * [应用管理](agent.md)
16 |
17 | * [自定义菜单](menu.md)
18 |
19 | * 消息推送
20 |
21 | * [发送应用消息](message.md)
22 | * [接收消息与事件](callback.md)
23 | * [发送消息到群聊会话](chat.md)
24 |
25 | * [素材管理](media.md)
26 |
27 | * 身份验证
28 |
29 | * [网页授权登录](auth.md)
30 |
31 | * 移动端SDK
32 |
33 | * [JS-SDK](jssdk.md)
34 |
35 | * OA数据接口
36 |
37 | * [打卡管理](checkin.md)
38 | * [审批管理](corp.md)
39 |
40 | * [电子发票](invoice.md)
41 |
42 | * 集成
43 |
44 | * [Laravel](laravel.md)
45 | * [ThinkPHP](thinkphp.md)
46 |
--------------------------------------------------------------------------------
/src/Http/Response.php:
--------------------------------------------------------------------------------
1 | getBody(), true);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Api/Corp.php:
--------------------------------------------------------------------------------
1 | $startTime,
23 | 'endtime' => $endTime
24 | ];
25 |
26 | if ($nextSPNum > 0) {
27 | $json['next_spnum'] = $nextSPNum;
28 | }
29 |
30 | return $this->httpClient->postJson('corp/getapprovaldata', $json);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/examples/invoice.php:
--------------------------------------------------------------------------------
1 | get('invoice');
7 |
8 | try {
9 | $invoice->getInfo('CARDID', 'ENCRYPTCODE');
10 | } catch (Exception $e) {
11 | }
12 |
13 | try {
14 | $invoice->updateStatus('CARDID', 'ENCRYPTCODE', 'INVOICE_REIMBURSE_INIT');
15 | } catch (Exception $e) {
16 | }
17 |
18 | try {
19 | $invoice->updateStatusBatch('OPENID', 'INVOICE_REIMBURSE_INIT', [
20 | ['card_id' => 'CARDID', 'encrypt_code' => 'ENCRYPTCODE']
21 | ]);
22 | } catch (Exception $e) {
23 | }
24 |
25 | try {
26 | $invoice->getInfoBatch([
27 | ['card_id' => 'CARDID', 'encrypt_code' => 'ENCRYPTCODE']
28 | ]);
29 | } catch (Exception $e) {
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/examples/department.php:
--------------------------------------------------------------------------------
1 | get('department');
7 |
8 | try {
9 | $department->create([
10 | 'name' => '广州研发中心',
11 | 'parentid' => 1,
12 | 'order' => 1,
13 | 'id' => 1024
14 | ]);
15 | } catch (Exception $e) {
16 | }
17 |
18 | try {
19 | $department->update([
20 | 'id' => 1024,
21 | 'name' => '广州研发中心',
22 | ]);
23 | } catch (Exception $e) {
24 | }
25 |
26 | try {
27 | $department->delete(1024);
28 | } catch (Exception $e) {
29 | }
30 |
31 | try {
32 | $department->list();
33 | } catch (Exception $e) {
34 | }
35 |
36 | try {
37 | $department->list(1);
38 | } catch (Exception $e) {
39 | }
40 |
--------------------------------------------------------------------------------
/docs/quick-start.md:
--------------------------------------------------------------------------------
1 | # 快速开始
2 |
3 | ## 安装
4 |
5 | ``` bash
6 | composer require pithyone/wechat
7 | ```
8 |
9 | ## 用法
10 |
11 | 初始化:
12 |
13 | ```php
14 | $app = new \WeWork\App(
15 | [
16 | 'corp_id' => 'xxxxxxxxxxxxxxxxxx',
17 | 'secret' => 'fsAQmTHCGvd5ZsRJnkr9jj9aCR2sqOKypAUw6D3Jy5Y',
18 | 'agent_id' => 0,
19 | 'token' => 'cxvhat5gfJW30J9YUsq',
20 | 'aes_key' => 'ULPUgqKDC8LTI1fRJ1jOxHrwbUV2QxR5jnSKrlh4meT',
21 | 'cache' => [
22 | 'path' => __DIR__.'/cache',
23 | ],
24 | 'log' => [
25 | 'file' => __DIR__.'/log/app.log',
26 | 'level' => 'debug',
27 | ],
28 | ]
29 | );
30 | ```
31 |
32 | 以获取 access_token 为例:
33 |
34 | ```php
35 | $app->get('token')->get();
36 | ```
37 |
--------------------------------------------------------------------------------
/tests/Message/FileTest.php:
--------------------------------------------------------------------------------
1 | file = new File('media_id');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForResponse()
29 | {
30 | $this->assertEquals([
31 | 'msgtype' => 'file',
32 | 'file' => [
33 | 'media_id' => 'media_id'
34 | ]
35 | ], $this->file->formatForResponse());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 | ./tests
14 |
15 |
16 |
17 |
18 | ./src
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/examples/tag.php:
--------------------------------------------------------------------------------
1 | get('tag');
7 |
8 | try {
9 | $tag->create('UI', 1024);
10 | } catch (Exception $e) {
11 | }
12 |
13 | try {
14 | $tag->update(1024, 'UI design');
15 | } catch (Exception $e) {
16 | }
17 |
18 | try {
19 | $tag->delete(1024);
20 | } catch (Exception $e) {
21 | }
22 |
23 | try {
24 | $tag->get(1024);
25 | } catch (Exception $e) {
26 | }
27 |
28 | try {
29 | $tag->addUsers(['tagid' => 1024, 'userlist' => ['user1', 'user2']]);
30 | } catch (Exception $e) {
31 | }
32 |
33 | try {
34 | $tag->delUsers(['tagid' => 1024, 'userlist' => ['user1', 'user2']]);
35 | } catch (Exception $e) {
36 | }
37 |
38 | try {
39 | $tag->list();
40 | } catch (Exception $e) {
41 | }
42 |
--------------------------------------------------------------------------------
/src/ApiCache/Token.php:
--------------------------------------------------------------------------------
1 | secret);
19 |
20 | return md5('wework.api.token.' . $unique);
21 | }
22 |
23 | /**
24 | * @return string
25 | */
26 | protected function getFromServer(): string
27 | {
28 | $data = $this->httpClient->get('gettoken', ['corpid' => $this->corpId, 'corpsecret' => $this->secret]);
29 |
30 | return $data['access_token'];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Message/MPNews.php:
--------------------------------------------------------------------------------
1 | articles = $articles;
18 | }
19 |
20 | /**
21 | * @return array
22 | */
23 | public function formatForResponse(): array
24 | {
25 | return [
26 | 'msgtype' => 'mpnews',
27 | 'mpnews' => [
28 | 'articles' => array_map(function (MPArticle $article) {
29 | return $article->formatForResponse();
30 | }, $this->articles)
31 | ]
32 | ];
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/tests/Message/MarkdownTest.php:
--------------------------------------------------------------------------------
1 | markdown = new Markdown('content');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForResponse()
29 | {
30 | $this->assertEquals([
31 | 'msgtype' => 'markdown',
32 | 'markdown' => [
33 | 'content' => 'content'
34 | ]
35 | ], $this->textCard->formatForResponse());
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/Http/ClientFactoryTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Client::class, ClientFactory::create(\Mockery::mock(LoggerInterface::class)));
19 | }
20 |
21 | /**
22 | * @return void
23 | */
24 | public function testCreateWithToken()
25 | {
26 | $this->assertInstanceOf(Client::class, ClientFactory::create(
27 | \Mockery::mock(LoggerInterface::class),
28 | \Mockery::mock(Token::class)
29 | ));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | getMethod($name);
20 | $method->setAccessible(true);
21 | return $method->invokeArgs($object, $args);
22 | }
23 |
24 | /**
25 | * @inheritdoc
26 | */
27 | protected function tearDown()
28 | {
29 | parent::tearDown();
30 |
31 | \Mockery::close();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Crypt/SHA1.php:
--------------------------------------------------------------------------------
1 | 最最最简单易用的企业微信SDK
9 |
10 | ## Requirement
11 |
12 | - PHP >= 7.1.3
13 | - SimpleXML PHP Extension
14 | - DOM PHP Extension
15 | - JSON PHP Extension
16 | - OpenSSL PHP Extension
17 |
18 | ## Installation
19 |
20 | ```bash
21 | composer require pithyone/wechat
22 | ```
23 |
24 | ## License
25 |
26 | [MIT](https://github.com/pithyone/wechat/blob/master/LICENSE)
27 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/pithyone/wechat)
2 | [](https://packagist.org/packages/pithyone/wechat)
3 | [](https://packagist.org/packages/pithyone/wechat)
4 | [](https://packagist.org/packages/pithyone/wechat)
5 |
6 | # WeWork
7 |
8 | > 最最最简单易用的企业微信SDK
9 |
10 | ## Requirement
11 |
12 | - PHP >= 7.1.3
13 | - SimpleXML PHP Extension
14 | - DOM PHP Extension
15 | - JSON PHP Extension
16 | - OpenSSL PHP Extension
17 |
18 | ## Installation
19 |
20 | ```bash
21 | composer require pithyone/wechat
22 | ```
23 |
24 | ## License
25 |
26 | [MIT](https://github.com/pithyone/wechat/blob/master/LICENSE)
27 |
--------------------------------------------------------------------------------
/src/Api/Message.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('message/send', array_merge(
25 | ['agentid' => $this->agentId],
26 | $receiver->get(),
27 | $responseMessage->formatForResponse(),
28 | ['safe' => (int)$safe]
29 | ));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/ApiCache/AbstractApiCache.php:
--------------------------------------------------------------------------------
1 | getCacheKey();
29 |
30 | $value = $this->cache->get($key);
31 |
32 | if ($refresh || !$value) {
33 | $value = $this->getFromServer();
34 | $this->cache->set($key, $value, 7100);
35 | }
36 |
37 | return $value;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Api/Agent.php:
--------------------------------------------------------------------------------
1 | httpClient->get('agent/get', ['agentid' => $this->agentId]);
20 | }
21 |
22 | /**
23 | * 设置应用
24 | *
25 | * @param array $json
26 | * @return array
27 | */
28 | public function set(array $json): array
29 | {
30 | return $this->httpClient->postJson('agent/set', array_merge(['agentid' => $this->agentId], $json));
31 | }
32 |
33 | /**
34 | * 获取应用列表
35 | *
36 | * @return array
37 | */
38 | public function list(): array
39 | {
40 | return $this->httpClient->get('agent/list');
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Http/ClientFactory.php:
--------------------------------------------------------------------------------
1 | push(Middleware::retry($logger));
22 | $stack->push(Middleware::response());
23 | $stack->push(Middleware::log($logger));
24 |
25 | if ($token instanceof Token) {
26 | $stack->push(Middleware::auth($token));
27 | }
28 |
29 | return new Client([
30 | 'base_uri' => 'https://qyapi.weixin.qq.com/cgi-bin/',
31 | 'handler' => $stack
32 | ]);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Message/Text.php:
--------------------------------------------------------------------------------
1 | content = $content;
18 | }
19 |
20 | /**
21 | * @return array
22 | */
23 | public function formatForReply(): array
24 | {
25 | return [
26 | 'MsgType' => 'text',
27 | 'Content' => $this->content
28 | ];
29 | }
30 |
31 | /**
32 | * @return array
33 | */
34 | public function formatForResponse(): array
35 | {
36 | return [
37 | 'msgtype' => 'text',
38 | 'text' => [
39 | 'content' => $this->content
40 | ]
41 | ];
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Api/Menu.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('menu/create', $json, ['agentid' => $this->agentId]);
21 | }
22 |
23 | /**
24 | * 获取菜单
25 | *
26 | * @return array
27 | */
28 | public function get(): array
29 | {
30 | return $this->httpClient->get('menu/get', ['agentid' => $this->agentId]);
31 | }
32 |
33 | /**
34 | * 删除菜单
35 | *
36 | * @return array
37 | */
38 | public function delete(): array
39 | {
40 | return $this->httpClient->get('menu/delete', ['agentid' => $this->agentId]);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/Message/TextCardTest.php:
--------------------------------------------------------------------------------
1 | textCard = new TextCard('title', 'description', 'url', 'btntxt');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForResponse()
29 | {
30 | $this->assertEquals([
31 | 'msgtype' => 'textcard',
32 | 'textcard' => [
33 | 'title' => 'title',
34 | 'description' => 'description',
35 | 'url' => 'url',
36 | 'btntxt' => 'btntxt'
37 | ]
38 | ], $this->textCard->formatForResponse());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Message/Image.php:
--------------------------------------------------------------------------------
1 | mediaId = $mediaId;
18 | }
19 |
20 | /**
21 | * @return array
22 | */
23 | public function formatForReply(): array
24 | {
25 | return [
26 | 'MsgType' => 'image',
27 | 'Image' => [
28 | 'MediaId' => $this->mediaId
29 | ]
30 | ];
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | public function formatForResponse(): array
37 | {
38 | return [
39 | 'msgtype' => 'image',
40 | 'image' => [
41 | 'media_id' => $this->mediaId
42 | ]
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Message/Voice.php:
--------------------------------------------------------------------------------
1 | mediaId = $mediaId;
18 | }
19 |
20 | /**
21 | * @return array
22 | */
23 | public function formatForReply(): array
24 | {
25 | return [
26 | 'MsgType' => 'voice',
27 | 'Voice' => [
28 | 'MediaId' => $this->mediaId
29 | ]
30 | ];
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | public function formatForResponse(): array
37 | {
38 | return [
39 | 'msgtype' => 'voice',
40 | 'voice' => [
41 | 'media_id' => $this->mediaId
42 | ]
43 | ];
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Http/HttpClientInterface.php:
--------------------------------------------------------------------------------
1 | article = new MPArticle('title', 'thumb_media_id', 'content', 'author', 'content_source_url', 'digest');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForResponse()
29 | {
30 | $this->assertEquals([
31 | 'title' => 'title',
32 | 'thumb_media_id' => 'thumb_media_id',
33 | 'author' => 'author',
34 | 'content_source_url' => 'content_source_url',
35 | 'content' => 'content',
36 | 'digest' => 'digest'
37 | ], $this->article->formatForResponse());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Http/ResponseTest.php:
--------------------------------------------------------------------------------
1 | shouldReceive('getBody')
18 | ->once()
19 | ->withNoArgs()
20 | ->andReturn('{"errcode":0,"errmsg":"foo"}');
21 |
22 | $this->assertEquals(['errcode' => 0, 'errmsg' => 'foo'], $response->toArray());
23 | }
24 |
25 | /**
26 | * @expectedException \InvalidArgumentException
27 | */
28 | public function testToArrayException()
29 | {
30 | $response = \Mockery::mock(Response::class . '[getBody]');
31 |
32 | $response->shouldReceive('getBody')
33 | ->once()
34 | ->withNoArgs()
35 | ->andReturn('');
36 |
37 | $response->toArray();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Message/TextTest.php:
--------------------------------------------------------------------------------
1 | text = new Text('content');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForReply()
29 | {
30 | $this->assertEquals([
31 | 'MsgType' => 'text',
32 | 'Content' => 'content'
33 | ], $this->text->formatForReply());
34 | }
35 |
36 | /**
37 | * @return void
38 | */
39 | public function testFormatForResponse()
40 | {
41 | $this->assertEquals([
42 | 'msgtype' => 'text',
43 | 'text' => [
44 | 'content' => 'content'
45 | ]
46 | ], $this->text->formatForResponse());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/examples/callback.php:
--------------------------------------------------------------------------------
1 | get('callback');
7 |
8 | switch ($callback->get('MsgType')) {
9 | case 'text':
10 | echo $callback->reply(new \WeWork\Message\Text($callback->get('Content'))); // 文本消息
11 | break;
12 | case 'image':
13 | echo $callback->reply(new \WeWork\Message\Image($callback->get('MediaId'))); // 图片消息
14 | break;
15 | case 'voice':
16 | echo $callback->reply(new \WeWork\Message\Voice($callback->get('MediaId'))); // 语音消息
17 | break;
18 | case 'video':
19 | echo $callback->reply(new \WeWork\Message\Video($callback->get('MediaId'), 'Title', 'Description')); // 视频消息
20 | break;
21 | default:
22 | $article = new \WeWork\Message\Article('title', 'http://www.soso.com', 'description', 'http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png');
23 | echo $callback->reply(new \WeWork\Message\News([$article])); // 图文消息
24 | break;
25 | }
26 |
--------------------------------------------------------------------------------
/docs/user.md:
--------------------------------------------------------------------------------
1 | # 成员管理
2 |
3 | ```php
4 | $user = $app->get('user');
5 | ```
6 |
7 | ## 创建成员
8 |
9 | ```php
10 | $user->create([
11 | 'userid' => 'zhangsan',
12 | 'name' => '张三',
13 | 'department' => [1],
14 | 'email' => 'zhangsan@gzdev.com'
15 | ]);
16 | ```
17 |
18 | ## 读取成员
19 |
20 | ```php
21 | $user->get('zhangsan');
22 | ```
23 |
24 | ## 更新成员
25 |
26 | ```php
27 | $user->update([
28 | 'userid' => 'zhangsan',
29 | 'name' => '张三三'
30 | ]);
31 | ```
32 |
33 | ## 删除成员
34 |
35 | ```php
36 | $user->delete('zhangsan');
37 | ```
38 |
39 | ## 批量删除成员
40 |
41 | ```php
42 | $user->batchDelete(['zhangsan', 'lisi']);
43 | ```
44 |
45 | ## 获取部门成员
46 |
47 | ```php
48 | $user->list(1);
49 | ```
50 |
51 | ## 获取部门成员详情
52 |
53 | ```php
54 | $user->list(1, true, true);
55 | ```
56 |
57 | ## userid转openid
58 |
59 | ```php
60 | $user->convertIdToOpenid('zhangsan');
61 | ```
62 |
63 | ## openid转userid
64 |
65 | ```php
66 | $user->convertOpenidToUserId('oDOGms-6yCnGrRovBj2yHij5JL6E');
67 | ```
68 |
69 | ## 二次验证
70 |
71 | ```php
72 | $user->authSuccess('zhangsan');
73 | ```
74 |
--------------------------------------------------------------------------------
/tests/Http/MiddlewareTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(\Closure::class, Middleware::auth(\Mockery::mock(Token::class)));
18 | }
19 |
20 | /**
21 | * @return void
22 | */
23 | public function testLog()
24 | {
25 | $this->assertInstanceOf(\Closure::class, Middleware::log(\Mockery::mock(LoggerInterface::class)));
26 | }
27 |
28 | /**
29 | * @return void
30 | */
31 | public function testRetry()
32 | {
33 | $this->assertInstanceOf(\Closure::class, Middleware::retry(\Mockery::mock(LoggerInterface::class)));
34 | }
35 |
36 | /**
37 | * @return void
38 | */
39 | public function testResponse()
40 | {
41 | $this->assertInstanceOf(\Closure::class, Middleware::response());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 王兵
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WeWork
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/tests/Message/ImageTest.php:
--------------------------------------------------------------------------------
1 | image = new Image('media_id');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForReply()
29 | {
30 | $this->assertEquals([
31 | 'MsgType' => 'image',
32 | 'Image' => [
33 | 'MediaId' => 'media_id'
34 | ]
35 | ], $this->image->formatForReply());
36 | }
37 |
38 | /**
39 | * @return void
40 | */
41 | public function testFormatForResponse()
42 | {
43 | $this->assertEquals([
44 | 'msgtype' => 'image',
45 | 'image' => [
46 | 'media_id' => 'media_id'
47 | ]
48 | ], $this->image->formatForResponse());
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/Message/VoiceTest.php:
--------------------------------------------------------------------------------
1 | voice = new Voice('media_id');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForReply()
29 | {
30 | $this->assertEquals([
31 | 'MsgType' => 'voice',
32 | 'Voice' => [
33 | 'MediaId' => 'media_id'
34 | ]
35 | ], $this->voice->formatForReply());
36 | }
37 |
38 | /**
39 | * @return void
40 | */
41 | public function testFormatForResponse()
42 | {
43 | $this->assertEquals([
44 | 'msgtype' => 'voice',
45 | 'voice' => [
46 | 'media_id' => 'media_id'
47 | ]
48 | ], $this->voice->formatForResponse());
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/Api/CRMTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->crm = new CRM();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testGet()
38 | {
39 | $this->httpClient->shouldReceive('get')
40 | ->once()
41 | ->with('crm/get_external_contact', ['external_userid' => 'foo'])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->crm->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->crm->getExternalContact('foo'));
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Crypt/ErrorCode.php:
--------------------------------------------------------------------------------
1 |
8 | * -40001: 签名验证错误
9 | * -40002: xml解析失败
10 | * -40003: sha加密生成签名失败
11 | * -40004: encodingAesKey 非法
12 | * -40005: corpid 校验错误
13 | * -40006: aes 加密失败
14 | * -40007: aes 解密失败
15 | * -40008: 解密后得到的buffer非法
16 | * -40009: base64加密失败
17 | * -40010: base64解密失败
18 | * -40011: 生成xml失败
19 | *
20 | */
21 | class ErrorCode
22 | {
23 | public static $OK = 0;
24 | public static $ValidateSignatureError = -40001;
25 | public static $ParseXmlError = -40002;
26 | public static $ComputeSignatureError = -40003;
27 | public static $IllegalAesKey = -40004;
28 | public static $ValidateCorpidError = -40005;
29 | public static $EncryptAESError = -40006;
30 | public static $DecryptAESError = -40007;
31 | public static $IllegalBuffer = -40008;
32 | public static $EncodeBase64Error = -40009;
33 | public static $DecodeBase64Error = -40010;
34 | public static $GenReturnXmlError = -40011;
35 | }
36 |
--------------------------------------------------------------------------------
/src/Laravel/CacheBridge.php:
--------------------------------------------------------------------------------
1 | toMinutes($ttl));
18 | }
19 |
20 | public function delete($key)
21 | {
22 | //
23 | }
24 |
25 | public function clear()
26 | {
27 | //
28 | }
29 |
30 | public function getMultiple($keys, $default = null)
31 | {
32 | //
33 | }
34 |
35 | public function setMultiple($values, $ttl = null)
36 | {
37 | //
38 | }
39 |
40 | public function deleteMultiple($keys)
41 | {
42 | //
43 | }
44 |
45 | public function has($key)
46 | {
47 | return Cache::has($key);
48 | }
49 |
50 | protected function toMinutes($ttl = null)
51 | {
52 | if (!is_null($ttl)) {
53 | return $ttl / 60;
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/docs/callback.md:
--------------------------------------------------------------------------------
1 | # 接收消息与事件
2 |
3 | 只需要配置 `corp_id` `token` `aes_key` 即可。
4 |
5 | ```php
6 | $callback = $app->get('callback');
7 | ```
8 |
9 | 示例:
10 |
11 | ```php
12 | switch ($callback->get('MsgType')) {
13 | case 'text': // 文本消息
14 | echo $callback->reply(new \WeWork\Message\Text($callback->get('Content')));
15 | break;
16 | case 'image': // 图片消息
17 | echo $callback->reply(new \WeWork\Message\Image($callback->get('MediaId')));
18 | break;
19 | case 'voice': // 语音消息
20 | echo $callback->reply(new \WeWork\Message\Voice($callback->get('MediaId')));
21 | break;
22 | case 'video': // 视频消息
23 | $video = new \WeWork\Message\Video($callback->get('MediaId'), 'Title', 'Description');
24 | echo $callback->reply($video);
25 | break;
26 | default: // 图文消息
27 | $article = new \WeWork\Message\Article(
28 | 'title',
29 | 'http://www.soso.com',
30 | 'description',
31 | 'http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png'
32 | );
33 | echo $callback->reply(new \WeWork\Message\News([$article]));
34 | break;
35 | }
36 | ```
37 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pithyone/wechat",
3 | "description": "企业微信SDK",
4 | "type": "library",
5 | "license": "MIT",
6 | "require": {
7 | "php": "^7.1.3",
8 | "ext-SimpleXML": "*",
9 | "ext-dom": "*",
10 | "ext-json": "*",
11 | "ext-openssl": "*",
12 | "doctrine/collections": "^1.5",
13 | "guzzlehttp/guzzle": "^6.3",
14 | "monolog/monolog": "^1.23",
15 | "symfony/cache": "^4.1",
16 | "symfony/dependency-injection": "^4.1",
17 | "symfony/http-foundation": "^4.1"
18 | },
19 | "require-dev": {
20 | "mikey179/vfsStream": "^1.6",
21 | "mockery/mockery": "^1.1",
22 | "phpunit/phpunit": "^6"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "WeWork\\": "src/"
27 | }
28 | },
29 | "autoload-dev": {
30 | "psr-4": {
31 | "WeWork\\Tests\\": "tests/"
32 | }
33 | },
34 | "config": {
35 | "sort-packages": true
36 | },
37 | "extra": {
38 | "laravel": {
39 | "providers": [
40 | "WeWork\\Laravel\\WeWorkServiceProvider"
41 | ]
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Api/Department.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('department/create', $json);
20 | }
21 |
22 | /**
23 | * 更新部门
24 | *
25 | * @param array $json
26 | * @return array
27 | */
28 | public function update(array $json): array
29 | {
30 | return $this->httpClient->postJson('department/update', $json);
31 | }
32 |
33 | /**
34 | * 删除部门
35 | *
36 | * @param int $id
37 | * @return array
38 | */
39 | public function delete(int $id): array
40 | {
41 | return $this->httpClient->get('department/delete', compact('id'));
42 | }
43 |
44 | /**
45 | * 获取部门列表
46 | *
47 | * @param int $id
48 | * @return array
49 | */
50 | public function list(int $id = 0): array
51 | {
52 | return $this->httpClient->get('department/list', $id > 0 ? compact('id') : []);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Api/CheckIn.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('checkin/getcheckinoption', ['datetime' => $datetime, 'useridlist' => $userIdList]);
25 | }
26 |
27 | /**
28 | * 获取打卡数据
29 | *
30 | * @param int $type
31 | * @param int $startTime
32 | * @param int $endTime
33 | * @param array $userIdList
34 | * @return array
35 | */
36 | public function getData(int $type, int $startTime, int $endTime, array $userIdList): array
37 | {
38 | return $this->httpClient->postJson('checkin/getcheckindata', [
39 | 'opencheckindatatype' => $type,
40 | 'starttime' => $startTime,
41 | 'endtime' => $endTime,
42 | 'useridlist' => $userIdList
43 | ]);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Message/News.php:
--------------------------------------------------------------------------------
1 | articles = $articles;
18 | }
19 |
20 | /**
21 | * @return array
22 | */
23 | public function formatForReply(): array
24 | {
25 | return [
26 | 'MsgType' => 'news',
27 | 'ArticleCount' => count($this->articles),
28 | 'Articles' => array_map(function (Article $article) {
29 | return $article->formatForReply();
30 | }, $this->articles)
31 | ];
32 | }
33 |
34 | /**
35 | * @return array
36 | */
37 | public function formatForResponse(): array
38 | {
39 | return [
40 | 'msgtype' => 'news',
41 | 'news' => [
42 | 'articles' => array_map(function (Article $article) {
43 | return $article->formatForResponse();
44 | }, $this->articles)
45 | ]
46 | ];
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Laravel/config.php:
--------------------------------------------------------------------------------
1 | 'xxxxxxxxxxxxxxxxx',
14 |
15 | /*
16 | |--------------------------------------------------------------------------
17 | | 默认应用
18 | |--------------------------------------------------------------------------
19 | |
20 | | 定义默认使用的应用。
21 | |
22 | */
23 |
24 | 'default' => 'contacts',
25 |
26 | /*
27 | |--------------------------------------------------------------------------
28 | | 应用列表
29 | |--------------------------------------------------------------------------
30 | |
31 | | 定义应用列表,如果某个应用的某些配置用不到或者不存在,可以不定义。
32 | |
33 | */
34 |
35 | 'agents' => [
36 | 'contacts' => [
37 | 'agent_id' => 0,
38 | 'secret' => 'xxxxxxxxxxxxxxxxx',
39 | 'token' => 'xxxxxxxxxxxxxxxxx',
40 | 'aes_key' => 'xxxxxxxxxxxxxxxxx',
41 | ],
42 |
43 | //...
44 | ],
45 | ];
46 |
--------------------------------------------------------------------------------
/src/Message/Receiver.php:
--------------------------------------------------------------------------------
1 | set('touser', $user);
19 | }
20 |
21 | /**
22 | * @param string|array $party
23 | * @return void
24 | */
25 | public function setParty($party): void
26 | {
27 | $this->set('toparty', $party);
28 | }
29 |
30 | /**
31 | * @param string|array $tag
32 | * @return void
33 | */
34 | public function setTag($tag): void
35 | {
36 | $this->set('totag', $tag);
37 | }
38 |
39 | /**
40 | * @return array
41 | */
42 | public function get(): array
43 | {
44 | return $this->receiver;
45 | }
46 |
47 | /**
48 | * @param string $key
49 | * @param mixed $value
50 | * @return void
51 | */
52 | private function set(string $key, $value): void
53 | {
54 | if (is_array($value)) {
55 | $value = implode('|', $value);
56 | }
57 |
58 | $this->receiver[$key] = $value;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/Message/ArticleTest.php:
--------------------------------------------------------------------------------
1 | article = new Article('title', 'url', 'description', 'picurl', 'btntxt');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForReply()
29 | {
30 | $this->assertEquals([
31 | 'Title' => 'title',
32 | 'Description' => 'description',
33 | 'PicUrl' => 'picurl',
34 | 'Url' => 'url'
35 | ], $this->article->formatForReply());
36 | }
37 |
38 | /**
39 | * @return void
40 | */
41 | public function testFormatForResponse()
42 | {
43 | $this->assertEquals([
44 | 'title' => 'title',
45 | 'description' => 'description',
46 | 'url' => 'url',
47 | 'picurl' => 'picurl',
48 | 'btntxt' => 'btntxt'
49 | ], $this->article->formatForResponse());
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/Crypt/PKCS7Encoder.php:
--------------------------------------------------------------------------------
1 | PKCS7Encoder::$block_size) {
45 | $pad = 0;
46 | }
47 | return substr($text, 0, (strlen($text) - $pad));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tests/Message/MPNewsTest.php:
--------------------------------------------------------------------------------
1 | news = new MPNews([new MPArticle('title', 'thumb_media_id', 'content', 'author', 'content_source_url', 'digest')]);
24 | }
25 |
26 | /**
27 | * @return void
28 | */
29 | public function testFormatForResponse()
30 | {
31 | $this->assertEquals([
32 | 'msgtype' => 'mpnews',
33 | 'mpnews' => [
34 | 'articles' => [
35 | [
36 | 'title' => 'title',
37 | 'thumb_media_id' => 'thumb_media_id',
38 | 'author' => 'author',
39 | 'content_source_url' => 'content_source_url',
40 | 'content' => 'content',
41 | 'digest' => 'digest'
42 | ]
43 | ]
44 | ]
45 | ], $this->news->formatForResponse());
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Message/TextCard.php:
--------------------------------------------------------------------------------
1 | title = $title;
36 | $this->description = $description;
37 | $this->url = $url;
38 | $this->btnTxt = $btnTxt;
39 | }
40 |
41 | /**
42 | * @return array
43 | */
44 | public function formatForResponse(): array
45 | {
46 | return [
47 | 'msgtype' => 'textcard',
48 | 'textcard' => [
49 | 'title' => $this->title,
50 | 'description' => $this->description,
51 | 'url' => $this->url,
52 | 'btntxt' => $this->btnTxt
53 | ]
54 | ];
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/tests/Message/VideoTest.php:
--------------------------------------------------------------------------------
1 | video = new Video('media_id', 'title', 'description');
23 | }
24 |
25 | /**
26 | * @return void
27 | */
28 | public function testFormatForReply()
29 | {
30 | $this->assertEquals([
31 | 'MsgType' => 'video',
32 | 'Video' => [
33 | 'MediaId' => 'media_id',
34 | 'Title' => 'title',
35 | 'Description' => 'description'
36 | ]
37 | ], $this->video->formatForReply());
38 | }
39 |
40 | /**
41 | * @return void
42 | */
43 | public function testFormatForResponse()
44 | {
45 | $this->assertEquals([
46 | 'msgtype' => 'video',
47 | 'video' => [
48 | 'media_id' => 'media_id',
49 | 'title' => 'title',
50 | 'description' => 'description'
51 | ]
52 | ], $this->video->formatForResponse());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/tests/ApiCache/TicketTest.php:
--------------------------------------------------------------------------------
1 | ticket = new Ticket();
24 | }
25 |
26 | /**
27 | * @return void
28 | * @throws \ReflectionException
29 | */
30 | public function testGetCacheKey()
31 | {
32 | $this->assertEquals('eca7e9ee3d2dc60c8d25d665119b7a3e', $this->callMethod($this->ticket, 'getCacheKey'));
33 | }
34 |
35 | /**
36 | * @return void
37 | * @throws \ReflectionException
38 | */
39 | public function testGetFromServer()
40 | {
41 | $httpClient = \Mockery::mock(HttpClientInterface::class);
42 |
43 | $httpClient->shouldReceive('get')
44 | ->once()
45 | ->with('ticket/get', ['type' => 'wx_card'])
46 | ->andReturn(['ticket' => 'foo']);
47 |
48 | $this->ticket->setHttpClient($httpClient);
49 |
50 | $this->assertEquals('foo', $this->callMethod($this->ticket, 'getFromServer'));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Api/Media.php:
--------------------------------------------------------------------------------
1 | httpClient->postFile('media/upload', $path, compact('type'));
22 | }
23 |
24 | /**
25 | * 获取临时素材
26 | *
27 | * @param string $id
28 | * @return StreamInterface
29 | */
30 | public function get(string $id): StreamInterface
31 | {
32 | return $this->httpClient->getStream('media/get', ['media_id' => $id]);
33 | }
34 |
35 | /**
36 | * 获取高清语音素材
37 | *
38 | * @param string $id
39 | * @return StreamInterface
40 | */
41 | public function getVoice(string $id): StreamInterface
42 | {
43 | return $this->httpClient->getStream('media/get/jssdk', ['media_id' => $id]);
44 | }
45 |
46 | /**
47 | * 上传图片
48 | *
49 | * @param string $path
50 | * @return array
51 | */
52 | public function uploadImg(string $path): array
53 | {
54 | return $this->httpClient->postFile('media/uploadimg', $path);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Crypt/XMLParse.php:
--------------------------------------------------------------------------------
1 | loadXML($xmltext);
23 | $array_e = $xml->getElementsByTagName('Encrypt');
24 | $encrypt = $array_e->item(0)->nodeValue;
25 | return array(0, $encrypt);
26 | } catch (\Exception $e) {
27 | return array(ErrorCode::$ParseXmlError, null, null);
28 | }
29 | }
30 |
31 | /**
32 | * 生成xml消息
33 | *
34 | * @param string $encrypt 加密后的消息密文
35 | * @param string $signature 安全签名
36 | * @param string $timestamp 时间戳
37 | * @param string $nonce 随机字符串
38 | * @return string
39 | */
40 | public function generate($encrypt, $signature, $timestamp, $nonce)
41 | {
42 | $format = "
43 |
44 |
45 | %s
46 |
47 | ";
48 | return sprintf($format, $encrypt, $signature, $timestamp, $nonce);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/ApiCache/JsTicketTest.php:
--------------------------------------------------------------------------------
1 | jsTicket = new JsApiTicket();
24 | }
25 |
26 | /**
27 | * @return void
28 | * @throws \ReflectionException
29 | */
30 | public function testGetCacheKey()
31 | {
32 | $this->jsTicket->setSecret('foo');
33 |
34 | $this->assertEquals('4ce02e279b2db945760a47d20069a2f2', $this->callMethod($this->jsTicket, 'getCacheKey'));
35 | }
36 |
37 | /**
38 | * @return void
39 | * @throws \ReflectionException
40 | */
41 | public function testGetFromServer()
42 | {
43 | $httpClient = \Mockery::mock(HttpClientInterface::class);
44 |
45 | $httpClient->shouldReceive('get')
46 | ->once()
47 | ->with('get_jsapi_ticket')
48 | ->andReturn(['ticket' => 'foo']);
49 |
50 | $this->jsTicket->setHttpClient($httpClient);
51 |
52 | $this->assertEquals('foo', $this->callMethod($this->jsTicket, 'getFromServer'));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/user.php:
--------------------------------------------------------------------------------
1 | get('user');
7 |
8 | try {
9 | $user->create([
10 | 'userid' => 'zhangsan',
11 | 'name' => '张三',
12 | 'department' => [1],
13 | 'email' => 'zhangsan@gzdev.com'
14 | ]);
15 | } catch (Exception $e) {
16 | }
17 |
18 | try {
19 | $user->get('zhangsan');
20 | } catch (Exception $e) {
21 | }
22 |
23 | try {
24 | $user->update([
25 | 'userid' => 'zhangsan',
26 | 'name' => '张三三'
27 | ]);
28 | } catch (Exception $e) {
29 | }
30 |
31 | try {
32 | $user->delete('zhangsan');
33 | } catch (Exception $e) {
34 | }
35 |
36 | try {
37 | $user->batchDelete(['zhangsan', 'lisi']);
38 | } catch (Exception $e) {
39 | }
40 |
41 | try {
42 | $user->list(1);
43 | } catch (Exception $e) {
44 | }
45 |
46 | try {
47 | $user->list(1, true, true);
48 | } catch (Exception $e) {
49 | }
50 |
51 | try {
52 | $user->convertIdToOpenid('zhangsan');
53 | } catch (Exception $e) {
54 | }
55 |
56 | try {
57 | $user->convertOpenidToUserId('oDOGms-6yCnGrRovBj2yHij5JL6E');
58 | } catch (Exception $e) {
59 | }
60 |
61 | try {
62 | $user->authSuccess('zhangsan');
63 | } catch (Exception $e) {
64 | }
65 |
66 | try {
67 | $user->getInfo('CODE');
68 | } catch (Exception $e) {
69 | }
70 |
71 | try {
72 | $user->getDetail('USER_TICKET');
73 | } catch (Exception $e) {
74 | }
75 |
--------------------------------------------------------------------------------
/tests/ApiCache/TokenTest.php:
--------------------------------------------------------------------------------
1 | token = new Token();
24 |
25 | $this->token->setSecret('foo');
26 | }
27 |
28 | /**
29 | * @return void
30 | * @throws \ReflectionException
31 | */
32 | public function testGetCacheKey()
33 | {
34 | $this->assertEquals('75e021ba614c0848bf2fbac06afec8ca', $this->callMethod($this->token, 'getCacheKey'));
35 | }
36 |
37 | /**
38 | * @return void
39 | * @throws \ReflectionException
40 | */
41 | public function testGetFromServer()
42 | {
43 | $this->token->setCorpId('bar');
44 |
45 | $httpClient = \Mockery::mock(HttpClientInterface::class);
46 |
47 | $httpClient->shouldReceive('get')
48 | ->once()
49 | ->with('gettoken', ['corpid' => 'bar', 'corpsecret' => 'foo'])
50 | ->andReturn(['access_token' => 'baz']);
51 |
52 | $this->token->setHttpClient($httpClient);
53 |
54 | $this->assertEquals('baz', $this->callMethod($this->token, 'getFromServer'));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Api/Batch.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('batch/invite', $json);
20 | }
21 |
22 | /**
23 | * 增量更新成员
24 | *
25 | * @param array $json
26 | * @return array
27 | */
28 | public function syncUser(array $json): array
29 | {
30 | return $this->httpClient->postJson('batch/syncuser', $json);
31 | }
32 |
33 | /**
34 | * 全量覆盖成员
35 | *
36 | * @param array $json
37 | * @return array
38 | */
39 | public function replaceUser(array $json): array
40 | {
41 | return $this->httpClient->postJson('batch/replaceuser', $json);
42 | }
43 |
44 | /**
45 | * 全量覆盖部门
46 | *
47 | * @param array $json
48 | * @return array
49 | */
50 | public function replaceParty(array $json): array
51 | {
52 | return $this->httpClient->postJson('batch/replaceparty', $json);
53 | }
54 |
55 | /**
56 | * 获取异步任务结果
57 | *
58 | * @param string $jobId
59 | * @return array
60 | */
61 | public function getResult(string $jobId): array
62 | {
63 | return $this->httpClient->get('batch/getresult', ['jobid' => $jobId]);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Api/AppChat.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('appchat/create', $json);
21 | }
22 |
23 | /**
24 | * 修改群聊会话
25 | *
26 | * @param array $json
27 | * @return array
28 | */
29 | public function update(array $json): array
30 | {
31 | return $this->httpClient->postJson('appchat/update', $json);
32 | }
33 |
34 | /**
35 | * 获取群聊会话
36 | *
37 | * @param string $id
38 | * @return array
39 | */
40 | public function get(string $id): array
41 | {
42 | return $this->httpClient->get('appchat/get', ['chatid' => $id]);
43 | }
44 |
45 | /**
46 | * 应用推送消息
47 | *
48 | * @param string $id
49 | * @param ResponseMessageInterface $responseMessage
50 | * @param bool $safe
51 | * @return array
52 | */
53 | public function send(string $id, ResponseMessageInterface $responseMessage, bool $safe = false): array
54 | {
55 | return $this->httpClient->postJson(
56 | 'appchat/send',
57 | array_merge(['chatid' => $id], $responseMessage->formatForResponse(), ['safe' => (int)$safe])
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Message/Video.php:
--------------------------------------------------------------------------------
1 | mediaId = $mediaId;
30 | $this->title = $title;
31 | $this->description = $description;
32 | }
33 |
34 | /**
35 | * @return array
36 | */
37 | public function formatForReply(): array
38 | {
39 | return [
40 | 'MsgType' => 'video',
41 | 'Video' => [
42 | 'MediaId' => $this->mediaId,
43 | 'Title' => $this->title,
44 | 'Description' => $this->description
45 | ]
46 | ];
47 | }
48 |
49 | /**
50 | * @return array
51 | */
52 | public function formatForResponse(): array
53 | {
54 | return [
55 | 'msgtype' => 'video',
56 | 'video' => [
57 | 'media_id' => $this->mediaId,
58 | 'title' => $this->title,
59 | 'description' => $this->description
60 | ]
61 | ];
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Laravel/WeWorkServiceProvider.php:
--------------------------------------------------------------------------------
1 | publishes(
25 | [
26 | __DIR__.'/config.php' => config_path('wework.php'),
27 | ]
28 | );
29 | }
30 |
31 | /**
32 | * Register services.
33 | *
34 | * @return void
35 | */
36 | public function register()
37 | {
38 | $this->app->singleton(
39 | 'wework',
40 | function ($app, $parameters) {
41 | $wework = array_merge($app['config']['wework'], $parameters);
42 |
43 | $agent = $wework['agents'][$wework['default']];
44 |
45 | $config = array_merge($agent, $wework);
46 |
47 | $config['log'] = LogBridge::class;
48 |
49 | $config['cache'] = CacheBridge::class;
50 |
51 | return new App($config);
52 | }
53 | );
54 | }
55 |
56 | /**
57 | * Get the services provided by the provider.
58 | *
59 | * @return array
60 | */
61 | public function provides()
62 | {
63 | return ['wework'];
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/tests/Api/CorpTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->corp = new Corp();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testGetApprovalData()
38 | {
39 | $this->httpClient->shouldReceive('postJson')
40 | ->once()
41 | ->with('corp/getapprovaldata', ['starttime' => 0, 'endtime' => 1])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->corp->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->corp->getApprovalData(0, 1));
47 | }
48 |
49 | /**
50 | * @return void
51 | */
52 | public function testGetApprovalDataWithNextSPNum()
53 | {
54 | $this->httpClient->shouldReceive('postJson')
55 | ->once()
56 | ->with('corp/getapprovaldata', ['starttime' => 0, 'endtime' => 1, 'next_spnum' => 2])
57 | ->andReturn(['errcode' => 0]);
58 |
59 | $this->corp->setHttpClient($this->httpClient);
60 |
61 | $this->assertEquals(['errcode' => 0], $this->corp->getApprovalData(0, 1, 2));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/Message/NewsTest.php:
--------------------------------------------------------------------------------
1 | news = new News([new Article('title', 'url', 'description', 'picurl', 'btntxt')]);
24 | }
25 |
26 | /**
27 | * @return void
28 | */
29 | public function testFormatForReply()
30 | {
31 | $this->assertEquals([
32 | 'MsgType' => 'news',
33 | 'ArticleCount' => 1,
34 | 'Articles' => [
35 | [
36 | 'Title' => 'title',
37 | 'Description' => 'description',
38 | 'PicUrl' => 'picurl',
39 | 'Url' => 'url'
40 | ]
41 | ]
42 | ], $this->news->formatForReply());
43 | }
44 |
45 | /**
46 | * @return void
47 | */
48 | public function testFormatForResponse()
49 | {
50 | $this->assertEquals([
51 | 'msgtype' => 'news',
52 | 'news' => [
53 | 'articles' => [
54 | [
55 | 'title' => 'title',
56 | 'description' => 'description',
57 | 'url' => 'url',
58 | 'picurl' => 'picurl',
59 | 'btntxt' => 'btntxt'
60 | ]
61 | ]
62 | ]
63 | ], $this->news->formatForResponse());
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Message/MPArticle.php:
--------------------------------------------------------------------------------
1 | title = $title;
48 | $this->thumbMediaId = $thumbMediaId;
49 | $this->content = $content;
50 | $this->author = $author;
51 | $this->contentSourceUrl = $contentSourceUrl;
52 | $this->digest = $digest;
53 | }
54 |
55 | /**
56 | * @return array
57 | */
58 | public function formatForResponse(): array
59 | {
60 | return [
61 | 'title' => $this->title,
62 | 'thumb_media_id' => $this->thumbMediaId,
63 | 'author' => $this->author,
64 | 'content_source_url' => $this->contentSourceUrl,
65 | 'content' => $this->content,
66 | 'digest' => $this->digest
67 | ];
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/JSSdk.php:
--------------------------------------------------------------------------------
1 | corpId;
22 |
23 | $timestamp = $this->getTimestamp();
24 |
25 | $nonceStr = $this->getNonceStr();
26 |
27 | $signature = sha1("jsapi_ticket={$this->jsApiTicket->get()}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}");
28 |
29 | return compact('appId', 'timestamp', 'nonceStr', 'signature');
30 | }
31 |
32 | /**
33 | * @return array
34 | * @throws \Psr\SimpleCache\InvalidArgumentException
35 | */
36 | public function getChooseInvoiceConfig(): array
37 | {
38 | $timestamp = $this->getTimestamp();
39 |
40 | $nonceStr = $this->getNonceStr();
41 |
42 | $array = ['INVOICE', $this->corpId, $timestamp, $nonceStr, $this->ticket->get()];
43 |
44 | sort($array, SORT_STRING);
45 |
46 | $str = implode($array);
47 |
48 | $cardSign = sha1($str);
49 |
50 | return compact('timestamp', 'nonceStr', 'cardSign');
51 | }
52 |
53 | /**
54 | * @return int
55 | */
56 | protected function getTimestamp(): int
57 | {
58 | return time();
59 | }
60 |
61 | /**
62 | * @return string
63 | */
64 | protected function getNonceStr(): string
65 | {
66 | return (new PrpCrypt(null))->getRandomStr();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/tests/Message/ReceiverTest.php:
--------------------------------------------------------------------------------
1 | receiver = new Receiver();
23 | }
24 |
25 | /**
26 | * @param $user
27 | * @param $expected
28 | * @return void
29 | *
30 | * @dataProvider setProvider
31 | */
32 | public function testSetUser($user, $expected)
33 | {
34 | $this->receiver->setUser($user);
35 |
36 | $this->assertEquals(['touser' => $expected], $this->receiver->get());
37 | }
38 |
39 | /**
40 | * @param $party
41 | * @param $expected
42 | * @return void
43 | *
44 | * @dataProvider setProvider
45 | */
46 | public function testSetParty($party, $expected)
47 | {
48 | $this->receiver->setParty($party);
49 |
50 | $this->assertEquals(['toparty' => $expected], $this->receiver->get());
51 | }
52 |
53 | /**
54 | * @param $tag
55 | * @param $expected
56 | * @return void
57 | *
58 | * @dataProvider setProvider
59 | */
60 | public function testSetTag($tag, $expected)
61 | {
62 | $this->receiver->setTag($tag);
63 |
64 | $this->assertEquals(['totag' => $expected], $this->receiver->get());
65 | }
66 |
67 | /**
68 | * @return array
69 | */
70 | public function setProvider()
71 | {
72 | return [
73 | ['foo', 'foo'],
74 | [['foo', 'bar'], 'foo|bar']
75 | ];
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Message/Article.php:
--------------------------------------------------------------------------------
1 | title = $title;
42 | $this->url = $url;
43 | $this->description = $description;
44 | $this->picUrl = $picUrl;
45 | $this->btnTxt = $btnTxt;
46 | }
47 |
48 | /**
49 | * @return array
50 | */
51 | public function formatForReply(): array
52 | {
53 | return [
54 | 'Title' => $this->title,
55 | 'Description' => $this->description,
56 | 'PicUrl' => $this->picUrl,
57 | 'Url' => $this->url
58 | ];
59 | }
60 |
61 | /**
62 | * @return array
63 | */
64 | public function formatForResponse(): array
65 | {
66 | return [
67 | 'title' => $this->title,
68 | 'description' => $this->description,
69 | 'url' => $this->url,
70 | 'picurl' => $this->picUrl,
71 | 'btntxt' => $this->btnTxt
72 | ];
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Api/Invoice.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('card/invoice/reimburse/getinvoiceinfo', ['card_id' => $id, 'encrypt_code' => $code]);
21 | }
22 |
23 | /**
24 | * 更新发票状态
25 | *
26 | * @param string $id
27 | * @param string $code
28 | * @param string $status
29 | * @return array
30 | */
31 | public function updateStatus(string $id, string $code, string $status): array
32 | {
33 | return $this->httpClient->postJson('card/invoice/reimburse/updateinvoicestatus', ['card_id' => $id, 'encrypt_code' => $code, 'reimburse_status' => $status]);
34 | }
35 |
36 | /**
37 | * 批量更新发票状态
38 | *
39 | * @param string $openid
40 | * @param string $status
41 | * @param array $list
42 | * @return array
43 | */
44 | public function updateStatusBatch(string $openid, string $status, array $list): array
45 | {
46 | return $this->httpClient->postJson('card/invoice/reimburse/updatestatusbatch', [
47 | 'openid' => $openid,
48 | 'reimburse_status' => $status,
49 | 'invoice_list' => $list
50 | ]);
51 | }
52 |
53 | /**
54 | * 批量查询电子发票
55 | *
56 | * @param array $list
57 | * @return array
58 | */
59 | public function getInfoBatch(array $list): array
60 | {
61 | return $this->httpClient->postJson('card/invoice/reimburse/getinvoiceinfobatch', ['item_list' => $list]);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/Api/MessageTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
32 |
33 | $this->message = new Message();
34 |
35 | $this->message->setAgentId('AGENTID');
36 | }
37 |
38 | /**
39 | * @return void
40 | */
41 | public function testSend()
42 | {
43 | $receiver = \Mockery::mock(Receiver::class);
44 |
45 | $receiver->shouldReceive('get')
46 | ->once()
47 | ->withNoArgs()
48 | ->andReturn(['foo' => 'bar']);
49 |
50 | $responseFormatter = \Mockery::mock(ResponseMessageInterface::class);
51 |
52 | $responseFormatter->shouldReceive('formatForResponse')
53 | ->once()
54 | ->withNoArgs()
55 | ->andReturn(['baz' => 'foo']);
56 |
57 | $this->httpClient->shouldReceive('postJson')
58 | ->once()
59 | ->with('message/send', ['agentid' => 'AGENTID', 'foo' => 'bar', 'baz' => 'foo', 'safe' => 0])
60 | ->andReturn(['errcode' => 0]);
61 |
62 | $this->message->setHttpClient($this->httpClient);
63 |
64 | $this->assertEquals(['errcode' => 0], $this->message->send($receiver, $responseFormatter));
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/tests/AppTest.php:
--------------------------------------------------------------------------------
1 | app = new App(['corp_id' => 'corpid', 'secret' => 'secret']);
22 | }
23 |
24 | /**
25 | * @param $expected
26 | * @param $id
27 | * @throws \Exception
28 | *
29 | * @dataProvider getProvider
30 | */
31 | public function testGet($expected, $id)
32 | {
33 | $this->assertInstanceOf($expected, $this->app->get($id));
34 | }
35 |
36 | /**
37 | * @return array
38 | */
39 | public function getProvider()
40 | {
41 | return [
42 | [\WeWork\ApiCache\Token::class, 'token'],
43 | [\WeWork\Callback::class, 'callback'],
44 | [\WeWork\Api\Agent::class, 'agent'],
45 | [\WeWork\Api\AppChat::class, 'appChat'],
46 | [\WeWork\Api\Batch::class, 'batch'],
47 | [\WeWork\Api\CheckIn::class, 'checkIn'],
48 | [\WeWork\Api\Corp::class, 'corp'],
49 | [\WeWork\Api\CRM::class, 'crm'],
50 | [\WeWork\Api\Department::class, 'department'],
51 | [\WeWork\Api\Invoice::class, 'invoice'],
52 | [\WeWork\Api\Media::class, 'media'],
53 | [\WeWork\Api\Menu::class, 'menu'],
54 | [\WeWork\Api\Message::class, 'message'],
55 | [\WeWork\Api\Tag::class, 'tag'],
56 | [\WeWork\Api\User::class, 'user'],
57 | [\WeWork\ApiCache\JsApiTicket::class, 'jsApiTicket'],
58 | [\WeWork\ApiCache\Ticket::class, 'ticket'],
59 | [\WeWork\JSSdk::class, 'jssdk'],
60 | ];
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/docs/message.md:
--------------------------------------------------------------------------------
1 | # 发送应用消息
2 |
3 | ```php
4 | $message = $app->get('message');
5 | ```
6 |
7 | 消息接收者
8 |
9 | ```php
10 | $receiver = new \WeWork\Message\Receiver();
11 | $receiver->setUser('userid1');
12 | $receiver->setParty([1024, 2048]);
13 | $receiver->setTag([1024, 2048]);
14 | ```
15 |
16 | ## 文本消息
17 |
18 | ```php
19 | $text = new \WeWork\Message\Text("你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看邮件中心视频实况,聪明避开排队。");
20 | $message->send($receiver, $text, true);
21 | ```
22 |
23 | ## 图片消息
24 |
25 | ```php
26 | $image = new \WeWork\Message\Image('MEDIA_ID');
27 | $message->send($receiver, $image);
28 | ```
29 |
30 | ## 语音消息
31 |
32 | ```php
33 | $voice = new \WeWork\Message\Voice('MEDIA_ID');
34 | $message->send($receiver, $voice);
35 | ```
36 |
37 | ## 视频消息
38 |
39 | ```php
40 | $video = new \WeWork\Message\Video('MEDIA_ID', 'Title', 'Description');
41 | $message->send($receiver, $video);
42 | ```
43 |
44 | ## 文件消息
45 |
46 | ```php
47 | $file = new \WeWork\Message\File('MEDIA_ID');
48 | $message->send($receiver, $file);
49 | ```
50 |
51 | ## 文本卡片消息
52 |
53 | ```php
54 | $textCard = new \WeWork\Message\TextCard('领奖通知', "2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
", 'URL', '更多');
55 | $message->send($receiver, $textCard);
56 | ```
57 |
58 | ## 图文消息
59 |
60 | ```php
61 | $news = new \WeWork\Message\News([
62 | new \WeWork\Message\Article('中秋节礼品领取', 'URL', '今年中秋节公司有豪礼相送', 'http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png', '更多')
63 | ]);
64 | $message->send($receiver, $news);
65 | ```
66 |
67 | ## 图文消息(mpnews)
68 |
69 | ```php
70 | $news = new \WeWork\Message\MPNews([
71 | new \WeWork\Message\MPArticle('Title', 'MEDIA_ID', 'Content', 'Author', 'URL', 'Digest description')
72 | ]);
73 | $message->send($receiver, $news);
74 | ```
75 |
--------------------------------------------------------------------------------
/src/Http/HttpClient.php:
--------------------------------------------------------------------------------
1 | client = $client;
21 | }
22 |
23 | /**
24 | * @param string $uri
25 | * @param array $query
26 | * @return array
27 | */
28 | public function get(string $uri, array $query = []): array
29 | {
30 | return $this->client->get($uri, compact('query'))->toArray();
31 | }
32 |
33 | /**
34 | * @param string $uri
35 | * @param array $query
36 | * @return StreamInterface
37 | */
38 | public function getStream(string $uri, array $query = []): StreamInterface
39 | {
40 | return $this->client->get($uri, compact('query'))->getBody();
41 | }
42 |
43 | /**
44 | * @param string $uri
45 | * @param array $json
46 | * @param array $query
47 | * @return array
48 | */
49 | public function postJson(string $uri, array $json = [], array $query = []): array
50 | {
51 | return $this->client->post($uri, compact('json', 'query'))->toArray();
52 | }
53 |
54 | /**
55 | * @param string $uri
56 | * @param string $path
57 | * @param array $query
58 | * @return array
59 | */
60 | public function postFile(string $uri, string $path, array $query = []): array
61 | {
62 | return $this->client->post($uri, array_merge([
63 | 'multipart' => [
64 | [
65 | 'name' => 'media',
66 | 'contents' => fopen($path, 'r')
67 | ]
68 | ]
69 | ], compact('query')))->toArray();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/ApiCache/AbstractApiCacheTest.php:
--------------------------------------------------------------------------------
1 | mock = $this->getMockForAbstractClass(AbstractApiCache::class);
25 |
26 | $this->mock->expects($this->once())
27 | ->method('getCacheKey')
28 | ->will($this->returnValue('foo'));
29 | }
30 |
31 | /**
32 | * @param bool $refresh
33 | * @param string $value
34 | * @param int $limit
35 | * @param string $expected
36 | * @return void
37 | *
38 | * @dataProvider getProvider
39 | */
40 | public function testGet($refresh, $value, $limit, $expected)
41 | {
42 | $cache = \Mockery::mock(CacheInterface::class);
43 |
44 | $cache->shouldReceive('get')
45 | ->once()
46 | ->with('foo')
47 | ->andReturn($value);
48 |
49 | $cache->shouldReceive('set')
50 | ->times($limit)
51 | ->with('foo', 'bar', 7100)
52 | ->andReturnSelf();
53 |
54 | $this->mock->setCache($cache);
55 |
56 | $this->mock->expects($this->exactly($limit))
57 | ->method('getFromServer')
58 | ->will($this->returnValue('bar'));
59 |
60 | $this->assertEquals($expected, $this->mock->get($refresh));
61 | }
62 |
63 | /**
64 | * @return array
65 | */
66 | public function getProvider()
67 | {
68 | return [
69 | [true, 'foo', 1, 'bar'],
70 | [true, '', 1, 'bar'],
71 | [false, 'foo', 0, 'foo'],
72 | [false, '', 1, 'bar'],
73 | ];
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/examples/message.php:
--------------------------------------------------------------------------------
1 | get('message');
7 |
8 | $receiver = new \WeWork\Message\Receiver();
9 | $receiver->setUser('userid1');
10 | $receiver->setParty([1024, 2048]);
11 | $receiver->setTag([1024, 2048]);
12 |
13 | try {
14 | $text = new \WeWork\Message\Text("你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看邮件中心视频实况,聪明避开排队。");
15 | $message->send($receiver, $text, true);
16 | } catch (Exception $e) {
17 | }
18 |
19 | try {
20 | $image = new \WeWork\Message\Image('MEDIA_ID');
21 | $message->send($receiver, $image);
22 | } catch (Exception $e) {
23 | }
24 |
25 | try {
26 | $voice = new \WeWork\Message\Voice('MEDIA_ID');
27 | $message->send($receiver, $voice);
28 | } catch (Exception $e) {
29 | }
30 |
31 | try {
32 | $video = new \WeWork\Message\Video('MEDIA_ID', 'Title', 'Description');
33 | $message->send($receiver, $video);
34 | } catch (Exception $e) {
35 | }
36 |
37 | try {
38 | $file = new \WeWork\Message\File('MEDIA_ID');
39 | $message->send($receiver, $file);
40 | } catch (Exception $e) {
41 | }
42 |
43 | try {
44 | $textCard = new \WeWork\Message\TextCard('领奖通知', "2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
", 'URL', '更多');
45 | $message->send($receiver, $textCard);
46 | } catch (Exception $e) {
47 | }
48 |
49 | try {
50 | $news = new \WeWork\Message\News([
51 | new \WeWork\Message\Article('中秋节礼品领取', 'URL', '今年中秋节公司有豪礼相送', 'http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png', '更多')
52 | ]);
53 | $message->send($receiver, $news);
54 | } catch (Exception $e) {
55 | }
56 |
57 | try {
58 | $news = new \WeWork\Message\MPNews([
59 | new \WeWork\Message\MPArticle('Title', 'MEDIA_ID', 'Content', 'Author', 'URL', 'Digest description')
60 | ]);
61 | $message->send($receiver, $news);
62 | } catch (Exception $e) {
63 | }
64 |
--------------------------------------------------------------------------------
/tests/Api/AgentTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->agent = new Agent();
32 |
33 | $this->agent->setAgentId('AGENTID');
34 | }
35 |
36 | /**
37 | * @return void
38 | */
39 | public function testGet()
40 | {
41 | $this->httpClient->shouldReceive('get')
42 | ->once()
43 | ->with('agent/get', ['agentid' => 'AGENTID'])
44 | ->andReturn(['errcode' => 0]);
45 |
46 | $this->agent->setHttpClient($this->httpClient);
47 |
48 | $this->assertEquals(['errcode' => 0], $this->agent->get());
49 | }
50 |
51 | /**
52 | * @return void
53 | */
54 | public function testSet()
55 | {
56 | $this->httpClient->shouldReceive('postJson')
57 | ->once()
58 | ->with('agent/set', ['agentid' => 'AGENTID', 'foo' => 'bar'])
59 | ->andReturn(['errcode' => 0]);
60 |
61 | $this->agent->setHttpClient($this->httpClient);
62 |
63 | $this->assertEquals(['errcode' => 0], $this->agent->set(['foo' => 'bar']));
64 | }
65 |
66 | /**
67 | * @return void
68 | */
69 | public function testList()
70 | {
71 | $this->httpClient->shouldReceive('get')
72 | ->once()
73 | ->with('agent/list')
74 | ->andReturn(['errcode' => 0]);
75 |
76 | $this->agent->setHttpClient($this->httpClient);
77 |
78 | $this->assertEquals(['errcode' => 0], $this->agent->list());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/tests/Api/MenuTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->menu = new Menu();
32 |
33 | $this->menu->setAgentId('AGENTID');
34 | }
35 |
36 | /**
37 | * @return void
38 | */
39 | public function testCreate()
40 | {
41 | $this->httpClient->shouldReceive('postJson')
42 | ->once()
43 | ->with('menu/create', ['bar' => 'baz'], ['agentid' => 'AGENTID'])
44 | ->andReturn(['errcode' => 0]);
45 |
46 | $this->menu->setHttpClient($this->httpClient);
47 |
48 | $this->assertEquals(['errcode' => 0], $this->menu->create(['bar' => 'baz']));
49 | }
50 |
51 | /**
52 | * @return void
53 | */
54 | public function testGet()
55 | {
56 | $this->httpClient->shouldReceive('get')
57 | ->once()
58 | ->with('menu/get', ['agentid' => 'AGENTID'])
59 | ->andReturn(['errcode' => 0]);
60 |
61 | $this->menu->setHttpClient($this->httpClient);
62 |
63 | $this->assertEquals(['errcode' => 0], $this->menu->get());
64 | }
65 |
66 | /**
67 | * @return void
68 | */
69 | public function testDelete()
70 | {
71 | $this->httpClient->shouldReceive('get')
72 | ->once()
73 | ->with('menu/delete', ['agentid' => 'AGENTID'])
74 | ->andReturn(['errcode' => 0]);
75 |
76 | $this->menu->setHttpClient($this->httpClient);
77 |
78 | $this->assertEquals(['errcode' => 0], $this->menu->delete());
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Api/Tag.php:
--------------------------------------------------------------------------------
1 | $name];
21 |
22 | if ($id > 0) {
23 | $json['tagid'] = $id;
24 | }
25 |
26 | return $this->httpClient->postJson('tag/create', $json);
27 | }
28 |
29 | /**
30 | * 更新标签名字
31 | *
32 | * @param int $id
33 | * @param string $name
34 | * @return array
35 | */
36 | public function update(int $id, string $name): array
37 | {
38 | return $this->httpClient->postJson('tag/update', ['tagid' => $id, 'tagname' => $name]);
39 | }
40 |
41 | /**
42 | * 删除标签
43 | *
44 | * @param int $id
45 | * @return array
46 | */
47 | public function delete(int $id): array
48 | {
49 | return $this->httpClient->get('tag/delete', ['tagid' => $id]);
50 | }
51 |
52 | /**
53 | * 获取标签成员
54 | *
55 | * @param int $id
56 | * @return array
57 | */
58 | public function get(int $id): array
59 | {
60 | return $this->httpClient->get('tag/get', ['tagid' => $id]);
61 | }
62 |
63 | /**
64 | * 增加标签成员
65 | *
66 | * @param array $json
67 | * @return array
68 | */
69 | public function addUsers(array $json): array
70 | {
71 | return $this->httpClient->postJson('tag/addtagusers', $json);
72 | }
73 |
74 | /**
75 | * 删除标签成员
76 | *
77 | * @param array $json
78 | * @return array
79 | */
80 | public function delUsers(array $json): array
81 | {
82 | return $this->httpClient->postJson('tag/deltagusers', $json);
83 | }
84 |
85 | /**
86 | * 获取标签列表
87 | *
88 | * @return array
89 | */
90 | public function list(): array
91 | {
92 | return $this->httpClient->get('tag/list');
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/examples/chat.php:
--------------------------------------------------------------------------------
1 | get('appChat');
7 |
8 | try {
9 | $chat->create([
10 | 'name' => 'NAME',
11 | 'owner' => 'userid1',
12 | 'userlist' => ['userid1', 'userid2'],
13 | 'chatid' => 'CHATID'
14 | ]);
15 | } catch (Exception $e) {
16 | }
17 |
18 | try {
19 | $chat->update([
20 | 'chatid' => 'CHATID',
21 | 'name' => 'NAME',
22 | 'owner' => 'userid2'
23 | ]);
24 | } catch (Exception $e) {
25 | }
26 |
27 | try {
28 | $chat->get('CHATID');
29 | } catch (Exception $e) {
30 | }
31 |
32 | try {
33 | $text = new \WeWork\Message\Text("你的快递已到,请携带工卡前往邮件中心领取。\n出发前可查看邮件中心视频实况,聪明避开排队。");
34 | $chat->send('CHATID', $text, true);
35 | } catch (Exception $e) {
36 | }
37 |
38 | try {
39 | $image = new \WeWork\Message\Image('MEDIA_ID');
40 | $chat->send('CHATID', $image);
41 | } catch (Exception $e) {
42 | }
43 |
44 | try {
45 | $voice = new \WeWork\Message\Voice('MEDIA_ID');
46 | $chat->send('CHATID', $voice);
47 | } catch (Exception $e) {
48 | }
49 |
50 | try {
51 | $video = new \WeWork\Message\Video('MEDIA_ID', 'Title', 'Description');
52 | $chat->send('CHATID', $video);
53 | } catch (Exception $e) {
54 | }
55 |
56 | try {
57 | $file = new \WeWork\Message\File('MEDIA_ID');
58 | $chat->send('CHATID', $file);
59 | } catch (Exception $e) {
60 | }
61 |
62 | try {
63 | $textCard = new \WeWork\Message\TextCard('领奖通知', "2016年9月26日
恭喜你抽中iPhone 7一台,领奖码:xxxx
请于2016年10月10日前联系行政同事领取
", 'URL', '更多');
64 | $chat->send('CHATID', $textCard);
65 | } catch (Exception $e) {
66 | }
67 |
68 | try {
69 | $news = new \WeWork\Message\News([
70 | new \WeWork\Message\Article('中秋节礼品领取', 'URL', '今年中秋节公司有豪礼相送', 'http://res.mail.qq.com/node/ww/wwopenmng/images/independent/doc/test_pic_msg1.png', '更多')
71 | ]);
72 | $chat->send('CHATID', $news);
73 | } catch (Exception $e) {
74 | }
75 |
76 | try {
77 | $news = new \WeWork\Message\MPNews([
78 | new \WeWork\Message\MPArticle('Title', 'MEDIA_ID', 'Content', 'Author', 'URL', 'Digest description')
79 | ]);
80 | $chat->send('CHATID', $news);
81 | } catch (Exception $e) {
82 | }
83 |
--------------------------------------------------------------------------------
/tests/JSSdkTest.php:
--------------------------------------------------------------------------------
1 | jssdk = \Mockery::mock(JSSdk::class . '[getTimestamp,getNonceStr]')
24 | ->shouldAllowMockingProtectedMethods();
25 |
26 | $this->jssdk->shouldReceive('getTimestamp')
27 | ->once()
28 | ->withNoArgs()
29 | ->andReturn(1234567890);
30 |
31 | $this->jssdk->shouldReceive('getNonceStr')
32 | ->once()
33 | ->withNoArgs()
34 | ->andReturn('noncestr');
35 | }
36 |
37 | /**
38 | * @return void
39 | * @throws \Psr\SimpleCache\InvalidArgumentException
40 | */
41 | public function testGetConfig()
42 | {
43 | $jsApiTicket = \Mockery::mock(JsApiTicket::class);
44 |
45 | $jsApiTicket->shouldReceive('get')
46 | ->once()
47 | ->withNoArgs()
48 | ->andReturn('jsapi_ticket');
49 |
50 | $this->jssdk->setCorpId('corpid');
51 |
52 | $this->jssdk->setJsApiTicket($jsApiTicket);
53 |
54 | $this->assertEquals([
55 | 'appId' => 'corpid',
56 | 'timestamp' => 1234567890,
57 | 'nonceStr' => 'noncestr',
58 | 'signature' => 'ae51fe229cba1316ed477ecbb06a53631ceaef2f'
59 | ], $this->jssdk->getConfig('url'));
60 | }
61 |
62 | /**
63 | * @return void
64 | * @throws \Psr\SimpleCache\InvalidArgumentException
65 | */
66 | public function testGetChooseInvoiceConfig()
67 | {
68 | $ticket = \Mockery::mock(Ticket::class);
69 |
70 | $ticket->shouldReceive('get')
71 | ->once()
72 | ->withNoArgs()
73 | ->andReturn('api_ticket');
74 |
75 | $this->jssdk->setCorpId('corpid');
76 |
77 | $this->jssdk->setTicket($ticket);
78 |
79 | $this->assertEquals([
80 | 'timestamp' => 1234567890,
81 | 'nonceStr' => 'noncestr',
82 | 'cardSign' => 'c1140f85f3fd19bd2f2e5716684086dca782774b'
83 | ], $this->jssdk->getChooseInvoiceConfig('url'));
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/tests/Api/MediaTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
31 |
32 | $this->media = new Media();
33 | }
34 |
35 | /**
36 | * @return void
37 | */
38 | public function testUpload()
39 | {
40 | $this->httpClient->shouldReceive('postFile')
41 | ->once()
42 | ->with('media/upload', 'foo', ['type' => 'file'])
43 | ->andReturn(['errcode' => 0]);
44 |
45 | $this->media->setHttpClient($this->httpClient);
46 |
47 | $this->assertEquals(['errcode' => 0], $this->media->upload('file', 'foo'));
48 | }
49 |
50 | /**
51 | * @return void
52 | */
53 | public function testGet()
54 | {
55 | $this->httpClient->shouldReceive('getStream')
56 | ->once()
57 | ->with('media/get', ['media_id' => 'foo'])
58 | ->andReturn(\Mockery::mock(StreamInterface::class));
59 |
60 | $this->media->setHttpClient($this->httpClient);
61 |
62 | $this->assertInstanceOf(StreamInterface::class, $this->media->get('foo'));
63 | }
64 |
65 | /**
66 | * @return void
67 | */
68 | public function testGetVoice()
69 | {
70 | $this->httpClient->shouldReceive('getStream')
71 | ->once()
72 | ->with('media/get/jssdk', ['media_id' => 'foo'])
73 | ->andReturn(\Mockery::mock(StreamInterface::class));
74 |
75 | $this->media->setHttpClient($this->httpClient);
76 |
77 | $this->assertInstanceOf(StreamInterface::class, $this->media->getVoice('foo'));
78 | }
79 |
80 | /**
81 | * @return void
82 | */
83 | public function testUploadImg()
84 | {
85 | $this->httpClient->shouldReceive('postFile')
86 | ->once()
87 | ->with('media/uploadimg', 'foo')
88 | ->andReturn(['errcode' => 0]);
89 |
90 | $this->media->setHttpClient($this->httpClient);
91 |
92 | $this->assertEquals(['errcode' => 0], $this->media->uploadImg('foo'));
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/tests/Api/InvoiceTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->invoice = new Invoice();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testGetInfo()
38 | {
39 | $this->httpClient->shouldReceive('postJson')
40 | ->once()
41 | ->with('card/invoice/reimburse/getinvoiceinfo', ['card_id' => 'foo', 'encrypt_code' => 'bar'])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->invoice->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->invoice->getInfo('foo', 'bar'));
47 | }
48 |
49 | /**
50 | * @return void
51 | */
52 | public function testUpdateStatus()
53 | {
54 | $this->httpClient->shouldReceive('postJson')
55 | ->once()
56 | ->with('card/invoice/reimburse/updateinvoicestatus', ['card_id' => 'foo', 'encrypt_code' => 'bar', 'reimburse_status' => 'baz'])
57 | ->andReturn(['errcode' => 0]);
58 |
59 | $this->invoice->setHttpClient($this->httpClient);
60 |
61 | $this->assertEquals(['errcode' => 0], $this->invoice->updateStatus('foo', 'bar', 'baz'));
62 | }
63 |
64 | /**
65 | * @return void
66 | */
67 | public function testUpdateStatusBatch()
68 | {
69 | $this->httpClient->shouldReceive('postJson')
70 | ->once()
71 | ->with('card/invoice/reimburse/updatestatusbatch', [
72 | 'openid' => 'foo',
73 | 'reimburse_status' => 'bar',
74 | 'invoice_list' => ['baz']
75 | ])
76 | ->andReturn(['errcode' => 0]);
77 |
78 | $this->invoice->setHttpClient($this->httpClient);
79 |
80 | $this->assertEquals(['errcode' => 0], $this->invoice->updateStatusBatch('foo', 'bar', ['baz']));
81 | }
82 |
83 | /**
84 | * @return void
85 | */
86 | public function testGetInfoBatch()
87 | {
88 | $this->httpClient->shouldReceive('postJson')
89 | ->once()
90 | ->with('card/invoice/reimburse/getinvoiceinfobatch', ['item_list' => ['foo']])
91 | ->andReturn(['errcode' => 0]);
92 |
93 | $this->invoice->setHttpClient($this->httpClient);
94 |
95 | $this->assertEquals(['errcode' => 0], $this->invoice->getInfoBatch(['foo']));
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/tests/Api/AppChatTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
31 |
32 | $this->appChat = new AppChat();
33 | }
34 |
35 | /**
36 | * @return void
37 | */
38 | public function testCreate()
39 | {
40 | $this->httpClient->shouldReceive('postJson')
41 | ->once()
42 | ->with('appchat/create', ['foo' => 'bar'])
43 | ->andReturn(['errcode' => 0]);
44 |
45 | $this->appChat->setHttpClient($this->httpClient);
46 |
47 | $this->assertEquals(['errcode' => 0], $this->appChat->create(['foo' => 'bar']));
48 | }
49 |
50 | /**
51 | * @return void
52 | */
53 | public function testUpdate()
54 | {
55 | $this->httpClient->shouldReceive('postJson')
56 | ->once()
57 | ->with('appchat/update', ['foo' => 'bar'])
58 | ->andReturn(['errcode' => 0]);
59 |
60 | $this->appChat->setHttpClient($this->httpClient);
61 |
62 | $this->assertEquals(['errcode' => 0], $this->appChat->update(['foo' => 'bar']));
63 | }
64 |
65 | /**
66 | * @return void
67 | */
68 | public function testGet()
69 | {
70 | $this->httpClient->shouldReceive('get')
71 | ->once()
72 | ->with('appchat/get', ['chatid' => 'foo'])
73 | ->andReturn(['errcode' => 0]);
74 |
75 | $this->appChat->setHttpClient($this->httpClient);
76 |
77 | $this->assertEquals(['errcode' => 0], $this->appChat->get('foo'));
78 | }
79 |
80 | /**
81 | * @return void
82 | */
83 | public function testSend()
84 | {
85 | $responseFormatter = \Mockery::mock(ResponseMessageInterface::class);
86 |
87 | $responseFormatter->shouldReceive('formatForResponse')
88 | ->once()
89 | ->withNoArgs()
90 | ->andReturn(['foo' => 'bar']);
91 |
92 | $this->httpClient->shouldReceive('postJson')
93 | ->once()
94 | ->with('appchat/send', ['chatid' => 'id', 'foo' => 'bar', 'safe' => 0])
95 | ->andReturn(['errcode' => 0]);
96 |
97 | $this->appChat->setHttpClient($this->httpClient);
98 |
99 | $this->assertEquals(['errcode' => 0], $this->appChat->send('id', $responseFormatter));
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/tests/Api/DepartmentTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->department = new Department();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testCreate()
38 | {
39 | $this->httpClient->shouldReceive('postJson')
40 | ->once()
41 | ->with('department/create', ['foo' => 'bar'])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->department->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->department->create(['foo' => 'bar']));
47 | }
48 |
49 | /**
50 | * @return void
51 | */
52 | public function testUpdate()
53 | {
54 | $this->httpClient->shouldReceive('postJson')
55 | ->once()
56 | ->with('department/update', ['foo' => 'bar'])
57 | ->andReturn(['errcode' => 0]);
58 |
59 | $this->department->setHttpClient($this->httpClient);
60 |
61 | $this->assertEquals(['errcode' => 0], $this->department->update(['foo' => 'bar']));
62 | }
63 |
64 | /**
65 | * @return void
66 | */
67 | public function testDelete()
68 | {
69 | $this->httpClient->shouldReceive('get')
70 | ->once()
71 | ->with('department/delete', ['id' => 1])
72 | ->andReturn(['errcode' => 0]);
73 |
74 | $this->department->setHttpClient($this->httpClient);
75 |
76 | $this->assertEquals(['errcode' => 0], $this->department->delete(1));
77 | }
78 |
79 | /**
80 | * @return void
81 | */
82 | public function testAllList()
83 | {
84 | $this->httpClient->shouldReceive('get')
85 | ->once()
86 | ->with('department/list', [])
87 | ->andReturn(['errcode' => 0]);
88 |
89 | $this->department->setHttpClient($this->httpClient);
90 |
91 | $this->assertEquals(['errcode' => 0], $this->department->list());
92 | }
93 |
94 | /**
95 | * @return void
96 | */
97 | public function testList()
98 | {
99 | $this->httpClient->shouldReceive('get')
100 | ->once()
101 | ->with('department/list', ['id' => 1])
102 | ->andReturn(['errcode' => 0]);
103 |
104 | $this->department->setHttpClient($this->httpClient);
105 |
106 | $this->assertEquals(['errcode' => 0], $this->department->list(1));
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/tests/Api/BatchTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->batch = new Batch();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testInvite()
38 | {
39 | $this->httpClient->shouldReceive('postJson')
40 | ->once()
41 | ->with('batch/invite', ['foo' => 'bar'])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->batch->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->batch->invite(['foo' => 'bar']));
47 | }
48 |
49 | /**
50 | * @return void
51 | */
52 | public function testSyncUser()
53 | {
54 | $this->httpClient->shouldReceive('postJson')
55 | ->once()
56 | ->with('batch/syncuser', ['foo' => 'bar'])
57 | ->andReturn(['errcode' => 0]);
58 |
59 | $this->batch->setHttpClient($this->httpClient);
60 |
61 | $this->assertEquals(['errcode' => 0], $this->batch->syncUser(['foo' => 'bar']));
62 | }
63 |
64 | /**
65 | * @return void
66 | */
67 | public function testReplaceUser()
68 | {
69 | $this->httpClient->shouldReceive('postJson')
70 | ->once()
71 | ->with('batch/replaceuser', ['foo' => 'bar'])
72 | ->andReturn(['errcode' => 0]);
73 |
74 | $this->batch->setHttpClient($this->httpClient);
75 |
76 | $this->assertEquals(['errcode' => 0], $this->batch->replaceUser(['foo' => 'bar']));
77 | }
78 |
79 | /**
80 | * @return void
81 | */
82 | public function testReplaceParty()
83 | {
84 | $this->httpClient->shouldReceive('postJson')
85 | ->once()
86 | ->with('batch/replaceparty', ['foo' => 'bar'])
87 | ->andReturn(['errcode' => 0]);
88 |
89 | $this->batch->setHttpClient($this->httpClient);
90 |
91 | $this->assertEquals(['errcode' => 0], $this->batch->replaceParty(['foo' => 'bar']));
92 | }
93 |
94 | /**
95 | * @return void
96 | */
97 | public function testGetResult()
98 | {
99 | $this->httpClient->shouldReceive('get')
100 | ->once()
101 | ->with('batch/getresult', ['jobid' => 'foo'])
102 | ->andReturn(['errcode' => 0]);
103 |
104 | $this->batch->setHttpClient($this->httpClient);
105 |
106 | $this->assertEquals(['errcode' => 0], $this->batch->getResult('foo'));
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/Crypt/PrpCrypt.php:
--------------------------------------------------------------------------------
1 | key = base64_decode($k . '=');
26 | $this->iv = substr($this->key, 0, 16);
27 | }
28 |
29 | /**
30 | * 加密
31 | *
32 | * @param string $text
33 | * @param string $corpid
34 | * @return array
35 | */
36 | public function encrypt($text, $corpid)
37 | {
38 | try {
39 | //拼接
40 | $text = $this->getRandomStr() . pack('N', strlen($text)) . $text . $corpid;
41 | //添加PKCS#7填充
42 | $pkc_encoder = new PKCS7Encoder;
43 | $text = $pkc_encoder->encode($text);
44 | //加密
45 | $encrypted = openssl_encrypt($text, 'AES-256-CBC', $this->key, OPENSSL_ZERO_PADDING, $this->iv);
46 | return [ErrorCode::$OK, $encrypted];
47 | } catch (\Exception $e) {
48 | return [ErrorCode::$EncryptAESError, null];
49 | }
50 | }
51 |
52 | /**
53 | * 解密
54 | *
55 | * @param $encrypted
56 | * @param $corpid
57 | * @return array
58 | */
59 | public function decrypt($encrypted, $corpid)
60 | {
61 | try {
62 | //解密
63 | $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', $this->key, OPENSSL_ZERO_PADDING, $this->iv);
64 | } catch (\Exception $e) {
65 | return [ErrorCode::$DecryptAESError, null];
66 | }
67 | try {
68 | //删除PKCS#7填充
69 | $pkc_encoder = new PKCS7Encoder;
70 | $result = $pkc_encoder->decode($decrypted);
71 | if (strlen($result) < 16) {
72 | return [];
73 | }
74 | //拆分
75 | $content = substr($result, 16, strlen($result));
76 | $len_list = unpack('N', substr($content, 0, 4));
77 | $xml_len = $len_list[1];
78 | $xml_content = substr($content, 4, $xml_len);
79 | $from_corpid = substr($content, $xml_len + 4);
80 | } catch (\Exception $e) {
81 | return [ErrorCode::$IllegalBuffer, null];
82 | }
83 | if ($from_corpid != $corpid) {
84 | return [ErrorCode::$ValidateCorpidError, null];
85 | }
86 | return [0, $xml_content];
87 | }
88 |
89 | /**
90 | * 生成随机字符串
91 | *
92 | * @return string
93 | */
94 | public function getRandomStr()
95 | {
96 | $str = '';
97 | $str_pol = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyl';
98 | $max = strlen($str_pol) - 1;
99 | for ($i = 0; $i < 16; $i++) {
100 | $str .= $str_pol[mt_rand(0, $max)];
101 | }
102 | return $str;
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/tests/Api/CheckInTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->checkIn = new CheckIn();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testGetOption()
38 | {
39 | $this->httpClient->shouldReceive('postJson')
40 | ->once()
41 | ->with('checkin/getcheckinoption', ['datetime' => 0, 'useridlist' => ['foo']])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->checkIn->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->checkIn->getOption(0, ['foo']));
47 | }
48 |
49 | /**
50 | * @return void
51 | */
52 | public function testGetDataWithCommuteType()
53 | {
54 | $this->httpClient->shouldReceive('postJson')
55 | ->once()
56 | ->with('checkin/getcheckindata', [
57 | 'opencheckindatatype' => 1,
58 | 'starttime' => 0,
59 | 'endtime' => 1,
60 | 'useridlist' => ['foo']
61 | ])
62 | ->andReturn(['errcode' => 0]);
63 |
64 | $this->checkIn->setHttpClient($this->httpClient);
65 |
66 | $this->assertEquals(['errcode' => 0], $this->checkIn->getData(CheckIn::TYPE_COMMUTE, 0, 1, ['foo']));
67 | }
68 |
69 | /**
70 | * @return void
71 | */
72 | public function testGetDataWithOutsideType()
73 | {
74 | $this->httpClient->shouldReceive('postJson')
75 | ->once()
76 | ->with('checkin/getcheckindata', [
77 | 'opencheckindatatype' => 2,
78 | 'starttime' => 0,
79 | 'endtime' => 1,
80 | 'useridlist' => ['foo']
81 | ])
82 | ->andReturn(['errcode' => 0]);
83 |
84 | $this->checkIn->setHttpClient($this->httpClient);
85 |
86 | $this->assertEquals(['errcode' => 0], $this->checkIn->getData(CheckIn::TYPE_OUTSIDE, 0, 1, ['foo']));
87 | }
88 |
89 | /**
90 | * @return void
91 | */
92 | public function testGetDataWithAllType()
93 | {
94 | $this->httpClient->shouldReceive('postJson')
95 | ->once()
96 | ->with('checkin/getcheckindata', [
97 | 'opencheckindatatype' => 3,
98 | 'starttime' => 0,
99 | 'endtime' => 1,
100 | 'useridlist' => ['foo']
101 | ])
102 | ->andReturn(['errcode' => 0]);
103 |
104 | $this->checkIn->setHttpClient($this->httpClient);
105 |
106 | $this->assertEquals(['errcode' => 0], $this->checkIn->getData(CheckIn::TYPE_ALL, 0, 1, ['foo']));
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/tests/CallbackTest.php:
--------------------------------------------------------------------------------
1 | assertEquals(null, $callback->get('foo'));
22 | }
23 |
24 | /**
25 | * @return void
26 | */
27 | public function testGet()
28 | {
29 | $request = Request::create('/', 'POST', [], [], [], [], 'content');
30 |
31 | $crypt = \Mockery::mock(WXBizMsgCrypt::class);
32 |
33 | $crypt->shouldReceive('DecryptMsg')
34 | ->once()
35 | ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::on(function (&$argument) {
36 | $argument = '';
37 | return true;
38 | }));
39 |
40 | $callback = new Callback($request, $crypt);
41 |
42 | $this->assertEquals(null, $callback->get('foo'));
43 |
44 | $this->assertEquals('bar', $callback->get('Foo'));
45 | }
46 |
47 | /**
48 | * @return void
49 | */
50 | public function testVerifyURLReply()
51 | {
52 | $request = Request::create('/?echostr=foo');
53 |
54 | $crypt = \Mockery::mock(WXBizMsgCrypt::class);
55 |
56 | $crypt->shouldReceive('VerifyURL')
57 | ->once()
58 | ->with(\Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::any(), \Mockery::on(function (&$argument) {
59 | $argument = 'text';
60 | return true;
61 | }));
62 |
63 | $callback = new Callback($request, $crypt);
64 |
65 | $this->assertEquals('text', $callback->reply(\Mockery::mock(ReplyMessageInterface::class)));
66 | }
67 |
68 | /**
69 | * @return void
70 | */
71 | public function testReply()
72 | {
73 | $request = Request::create('/');
74 |
75 | $request->attributes->set('ToUserName', 'toUser');
76 | $request->attributes->set('FromUserName', 'fromUser');
77 | $request->attributes->set('CreateTime', 1348831860);
78 |
79 | $replyMessage = \Mockery::mock(ReplyMessageInterface::class);
80 |
81 | $replyMessage->shouldReceive('formatForReply')
82 | ->once()
83 | ->withNoArgs()
84 | ->andReturn(['Foo' => 'bar']);
85 |
86 | $crypt = \Mockery::mock(WXBizMsgCrypt::class);
87 |
88 | $crypt->shouldReceive('EncryptMsg')
89 | ->once()
90 | ->with('1348831860', \Mockery::any(), \Mockery::any(), \Mockery::on(function (&$argument) {
91 | $argument = 'xml';
92 | return true;
93 | }));
94 |
95 | $callback = new Callback($request, $crypt);
96 |
97 | $this->assertEquals('xml', $callback->reply($replyMessage));
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/Api/User.php:
--------------------------------------------------------------------------------
1 | httpClient->postJson('user/create', $json);
20 | }
21 |
22 | /**
23 | * 读取成员
24 | *
25 | * @param string $id
26 | * @return array
27 | */
28 | public function get(string $id): array
29 | {
30 | return $this->httpClient->get('user/get', ['userid' => $id]);
31 | }
32 |
33 | /**
34 | * 更新成员
35 | *
36 | * @param array $json
37 | * @return array
38 | */
39 | public function update(array $json): array
40 | {
41 | return $this->httpClient->postJson('user/update', $json);
42 | }
43 |
44 | /**
45 | * 删除成员
46 | *
47 | * @param string $id
48 | * @return array
49 | */
50 | public function delete(string $id): array
51 | {
52 | return $this->httpClient->get('user/delete', ['userid' => $id]);
53 | }
54 |
55 | /**
56 | * 批量删除成员
57 | *
58 | * @param array $idList
59 | * @return array
60 | */
61 | public function batchDelete(array $idList): array
62 | {
63 | return $this->httpClient->postJson('user/batchdelete', ['useridlist' => $idList]);
64 | }
65 |
66 | /**
67 | * 获取部门成员
68 | *
69 | * @param int $departmentId
70 | * @param bool $fetchChild
71 | * @param bool $needDetail
72 | * @return array
73 | */
74 | public function list(int $departmentId, bool $fetchChild = false, bool $needDetail = false): array
75 | {
76 | $uri = 'user/' . ($needDetail ? 'list' : 'simplelist');
77 |
78 | return $this->httpClient->get($uri, ['department_id' => $departmentId, 'fetch_child' => (int)$fetchChild]);
79 | }
80 |
81 | /**
82 | * userid转openid
83 | *
84 | * @param string $id
85 | * @return array
86 | */
87 | public function convertIdToOpenid(string $id): array
88 | {
89 | return $this->httpClient->postJson('user/convert_to_openid', ['userid' => $id]);
90 | }
91 |
92 | /**
93 | * openid转userid
94 | *
95 | * @param string $openid
96 | * @return array
97 | */
98 | public function convertOpenidToUserId(string $openid): array
99 | {
100 | return $this->httpClient->postJson('user/convert_to_userid', compact('openid'));
101 | }
102 |
103 | /**
104 | * 二次验证
105 | *
106 | * @param string $id
107 | * @return array
108 | */
109 | public function authSuccess(string $id): array
110 | {
111 | return $this->httpClient->get('user/authsucc', ['userid' => $id]);
112 | }
113 |
114 | /**
115 | * 根据code获取成员信息
116 | *
117 | * @param string $code
118 | * @return array
119 | */
120 | public function getInfo(string $code): array
121 | {
122 | return $this->httpClient->get('user/getuserinfo', compact('code'));
123 | }
124 |
125 | /**
126 | * 使用user_ticket获取成员详情
127 | *
128 | * @param string $ticket
129 | * @return array
130 | */
131 | public function getDetail(string $ticket): array
132 | {
133 | return $this->httpClient->postJson('user/getuserdetail', ['user_ticket' => $ticket]);
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/Http/Middleware.php:
--------------------------------------------------------------------------------
1 | withUri(Uri::withQueryValue($request->getUri(), 'access_token', $token->get()));
28 | });
29 | }
30 |
31 | /**
32 | * @param LoggerInterface $logger
33 | * @return callable
34 | */
35 | public static function log(LoggerInterface $logger)
36 | {
37 | return \GuzzleHttp\Middleware::log($logger, new MessageFormatter(MessageFormatter::DEBUG), LogLevel::DEBUG);
38 | }
39 |
40 | /**
41 | * @param LoggerInterface $logger
42 | * @return callable
43 | */
44 | public static function retry(LoggerInterface $logger)
45 | {
46 | return \GuzzleHttp\Middleware::retry(function (
47 | $retries,
48 | Request $request,
49 | Response $response = null,
50 | RequestException $exception = null
51 | ) use ($logger) {
52 | if ($retries >= self::RETRY_MAX_RETRIES) {
53 | return false;
54 | }
55 |
56 | if (!(self::isServerError($response) || self::isConnectError($exception))) {
57 | return false;
58 | }
59 |
60 | $logger->warning(
61 | sprintf(
62 | 'Retrying %s %s %s/%s, %s',
63 | $request->getMethod(),
64 | $request->getUri(),
65 | $retries + 1,
66 | self::RETRY_MAX_RETRIES,
67 | $response ? 'status code: ' . $response->getStatusCode() : $exception->getMessage()
68 | ),
69 | [$request->getHeader('Host')[0]]
70 | );
71 |
72 | return true;
73 | });
74 | }
75 |
76 | /**
77 | * @return callable
78 | */
79 | public static function response()
80 | {
81 | return \GuzzleHttp\Middleware::mapResponse(function (ResponseInterface $response) {
82 | return new Response(
83 | $response->getStatusCode(),
84 | $response->getHeaders(),
85 | $response->getBody(),
86 | $response->getProtocolVersion(),
87 | $response->getReasonPhrase()
88 | );
89 | });
90 | }
91 |
92 | /**
93 | * @param Response $response
94 | *
95 | * @return bool
96 | */
97 | private static function isServerError(Response $response = null)
98 | {
99 | return $response && $response->getStatusCode() >= 500;
100 | }
101 |
102 | /**
103 | * @param RequestException $exception
104 | *
105 | * @return bool
106 | */
107 | private static function isConnectError(RequestException $exception = null)
108 | {
109 | return $exception instanceof ConnectException;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/tests/Http/HttpClientTest.php:
--------------------------------------------------------------------------------
1 | client = \Mockery::mock(Client::class);
27 | }
28 |
29 | /**
30 | * @return void
31 | */
32 | public function testGet()
33 | {
34 | $response = \Mockery::mock();
35 |
36 | $response->shouldReceive('toArray')
37 | ->once()
38 | ->withNoArgs()
39 | ->andReturn(['foo' => 'bar']);
40 |
41 | $this->client->shouldReceive('get')
42 | ->once()
43 | ->with('foo', ['query' => ['bar' => 'baz']])
44 | ->andReturn($response);
45 |
46 | $httpClient = new HttpClient($this->client);
47 |
48 | $this->assertEquals(['foo' => 'bar'], $httpClient->get('foo', ['bar' => 'baz']));
49 | }
50 |
51 | /**
52 | * @return void
53 | */
54 | public function testGetStream()
55 | {
56 | $response = \Mockery::mock();
57 |
58 | $response->shouldReceive('getBody')
59 | ->once()
60 | ->withNoArgs()
61 | ->andReturn(\Mockery::mock(StreamInterface::class));
62 |
63 | $this->client->shouldReceive('get')
64 | ->once()
65 | ->with('foo', ['query' => ['bar' => 'baz']])
66 | ->andReturn($response);
67 |
68 | $httpClient = new HttpClient($this->client);
69 |
70 | $this->assertInstanceOf(StreamInterface::class, $httpClient->getStream('foo', ['bar' => 'baz']));
71 | }
72 |
73 | /**
74 | * @return void
75 | */
76 | public function testPostJson()
77 | {
78 | $response = \Mockery::mock();
79 |
80 | $response->shouldReceive('toArray')
81 | ->once()
82 | ->withNoArgs()
83 | ->andReturn(['foo' => 'bar']);
84 |
85 | $this->client->shouldReceive('post')
86 | ->once()
87 | ->with('foo', ['json' => ['bar'], 'query' => ['baz']])
88 | ->andReturn($response);
89 |
90 | $httpClient = new HttpClient($this->client);
91 |
92 | $this->assertEquals(['foo' => 'bar'], $httpClient->postJson('foo', ['bar'], ['baz']));
93 | }
94 |
95 | /**
96 | * @return void
97 | */
98 | public function testPostFile()
99 | {
100 | $path = vfsStream::setup('root', null, ['foo.png' => 'bar'])->getChild('foo.png')->url();
101 |
102 | $response = \Mockery::mock();
103 |
104 | $response->shouldReceive('toArray')
105 | ->once()
106 | ->withNoArgs()
107 | ->andReturn(['foo' => 'bar']);
108 |
109 | $this->client->shouldReceive('post')
110 | ->once()
111 | ->with('foo', \Mockery::on(function ($argument) {
112 | return $argument['multipart'][0]['name'] === 'media' && is_resource($argument['multipart'][0]['contents']) && $argument['query'] === ['bar' => 'baz'];
113 | }))
114 | ->andReturn($response);
115 |
116 | $httpClient = new HttpClient($this->client);
117 |
118 | $this->assertEquals(['foo' => 'bar'], $httpClient->postFile('foo', $path, ['bar' => 'baz']));
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/tests/Crypt/WXBizMsgCryptTest.php:
--------------------------------------------------------------------------------
1 | wxcpt = new WXBizMsgCrypt($token, $encodingAesKey, $corpId);
28 | }
29 |
30 | /**
31 | * @return void
32 | */
33 | public function testVerifyURL()
34 | {
35 | $sVerifyMsgSig = "5c45ff5e21c57e6ad56bac8758b79b1d9ac89fd3";
36 | $sVerifyTimeStamp = "1409659589";
37 | $sVerifyNonce = "263014780";
38 | $sVerifyEchoStr = "P9nAzCzyDtyTWESHep1vC5X9xho/qYX3Zpb4yKa9SKld1DsH3Iyt3tP3zNdtp+4RPcs8TgAE7OaBO+FZXvnaqQ==";
39 |
40 | $sEchoStr = '';
41 |
42 | $this->wxcpt->VerifyURL($sVerifyMsgSig, $sVerifyTimeStamp, $sVerifyNonce, $sVerifyEchoStr, $sEchoStr);
43 |
44 | $this->assertEquals('1616140317555161061', $sEchoStr);
45 | }
46 |
47 | /**
48 | * @return void
49 | */
50 | public function testDecryptMsg()
51 | {
52 | $sReqMsgSig = "477715d11cdb4164915debcba66cb864d751f3e6";
53 | $sReqTimeStamp = "1409659813";
54 | $sReqNonce = "1372623149";
55 | $sReqData = "";
56 |
57 | $sMsg = ''; // 解析之后的明文
58 |
59 | $this->wxcpt->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sReqData, $sMsg);
60 |
61 | $this->assertEquals("
62 |
63 | 1409659813
64 |
65 |
66 | 4561255354251345929
67 | 218
68 | ", $sMsg);
69 | }
70 |
71 | /**
72 | * @return void
73 | */
74 | public function testEncryptMsg()
75 | {
76 | $sReqTimeStamp = "1409659813";
77 | $sReqNonce = "1372623149";
78 |
79 | // 需要发送的明文
80 | $sRespData = "13488318601234567890123456128";
81 |
82 | $sEncryptMsg = ''; //xml格式的密文
83 |
84 | $this->wxcpt->EncryptMsg($sRespData, $sReqTimeStamp, $sReqNonce, $sEncryptMsg);
85 |
86 | $xml = new DOMDocument();
87 | $xml->loadXML($sEncryptMsg);
88 | $sReqMsgSig = $xml->getElementsByTagName('MsgSignature')->item(0)->nodeValue;
89 |
90 | $sMsg = ''; // 解析之后的明文
91 |
92 | $this->wxcpt->DecryptMsg($sReqMsgSig, $sReqTimeStamp, $sReqNonce, $sEncryptMsg, $sMsg);
93 |
94 | $this->assertEquals($sRespData, $sMsg);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/Api/TagTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->tag = new Tag();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testCreateWithNoId()
38 | {
39 | $this->httpClient->shouldReceive('postJson')
40 | ->once()
41 | ->with('tag/create', ['tagname' => 'foo'])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->tag->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->tag->create('foo'));
47 | }
48 |
49 | /**
50 | * @param string $name
51 | * @param string $id
52 | * @param array $json
53 | * @return void
54 | *
55 | * @dataProvider createProvider
56 | */
57 | public function testCreateWithId($name, $id, $json)
58 | {
59 | $this->httpClient->shouldReceive('postJson')
60 | ->once()
61 | ->with('tag/create', $json)
62 | ->andReturn(['errcode' => 0]);
63 |
64 | $this->tag->setHttpClient($this->httpClient);
65 |
66 | $this->assertEquals(['errcode' => 0], $this->tag->create($name, $id));
67 | }
68 |
69 | /**
70 | * @return array
71 | */
72 | public function createProvider()
73 | {
74 | return [
75 | ['foo', 0, ['tagname' => 'foo']],
76 | ['bar', 1, ['tagname' => 'bar', 'tagid' => 1]]
77 | ];
78 | }
79 |
80 | /**
81 | * @return void
82 | */
83 | public function testUpdate()
84 | {
85 | $this->httpClient->shouldReceive('postJson')
86 | ->once()
87 | ->with('tag/update', ['tagid' => 1, 'tagname' => 'foo'])
88 | ->andReturn(['errcode' => 0]);
89 |
90 | $this->tag->setHttpClient($this->httpClient);
91 |
92 | $this->assertEquals(['errcode' => 0], $this->tag->update(1, 'foo'));
93 | }
94 |
95 | /**
96 | * @return void
97 | */
98 | public function testDelete()
99 | {
100 | $this->httpClient->shouldReceive('get')
101 | ->once()
102 | ->with('tag/delete', ['tagid' => 1])
103 | ->andReturn(['errcode' => 0]);
104 |
105 | $this->tag->setHttpClient($this->httpClient);
106 |
107 | $this->assertEquals(['errcode' => 0], $this->tag->delete(1));
108 | }
109 |
110 | /**
111 | * @return void
112 | */
113 | public function testGet()
114 | {
115 | $this->httpClient->shouldReceive('get')
116 | ->once()
117 | ->with('tag/get', ['tagid' => 1])
118 | ->andReturn(['errcode' => 0]);
119 |
120 | $this->tag->setHttpClient($this->httpClient);
121 |
122 | $this->assertEquals(['errcode' => 0], $this->tag->get(1));
123 | }
124 |
125 | /**
126 | * @return void
127 | */
128 | public function testAddUsers()
129 | {
130 | $this->httpClient->shouldReceive('postJson')
131 | ->once()
132 | ->with('tag/addtagusers', ['foo' => 'bar'])
133 | ->andReturn(['errcode' => 0]);
134 |
135 | $this->tag->setHttpClient($this->httpClient);
136 |
137 | $this->assertEquals(['errcode' => 0], $this->tag->addUsers(['foo' => 'bar']));
138 | }
139 |
140 | /**
141 | * @return void
142 | */
143 | public function testDelUsers()
144 | {
145 | $this->httpClient->shouldReceive('postJson')
146 | ->once()
147 | ->with('tag/deltagusers', ['foo' => 'bar'])
148 | ->andReturn(['errcode' => 0]);
149 |
150 | $this->tag->setHttpClient($this->httpClient);
151 |
152 | $this->assertEquals(['errcode' => 0], $this->tag->delUsers(['foo' => 'bar']));
153 | }
154 |
155 | /**
156 | * @return void
157 | */
158 | public function testList()
159 | {
160 | $this->httpClient->shouldReceive('get')
161 | ->once()
162 | ->with('tag/list')
163 | ->andReturn(['errcode' => 0]);
164 |
165 | $this->tag->setHttpClient($this->httpClient);
166 |
167 | $this->assertEquals(['errcode' => 0], $this->tag->list());
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/Callback.php:
--------------------------------------------------------------------------------
1 | request = $request;
29 | $this->crypt = $crypt;
30 | }
31 |
32 | /**
33 | * @param string $key
34 | * @return mixed
35 | */
36 | public function get(string $key)
37 | {
38 | if (!$this->request->getContent()) {
39 | return null;
40 | }
41 |
42 | if (!$this->request->attributes->has($key)) {
43 | $this->initializeAttribute();
44 | }
45 |
46 | return $this->request->attributes->get($key);
47 | }
48 |
49 | /**
50 | * @return void
51 | */
52 | private function initializeAttribute(): void
53 | {
54 | $data = '';
55 |
56 | $this->crypt->DecryptMsg(
57 | $this->request->query->get('msg_signature'),
58 | $this->request->query->get('timestamp'),
59 | $this->request->query->get('nonce'),
60 | $this->request->getContent(),
61 | $data
62 | );
63 |
64 | if ($data) {
65 | $xml = new SimpleXMLElement($data);
66 |
67 | foreach ($xml as $key => $value) {
68 | $this->request->attributes->set("$key", "$value");
69 | }
70 | }
71 | }
72 |
73 | /**
74 | * @return string
75 | */
76 | private function decryptEchoStr(): string
77 | {
78 | $plainText = '';
79 |
80 | $this->crypt->VerifyURL(
81 | $this->request->query->get('msg_signature'),
82 | $this->request->query->get('timestamp'),
83 | $this->request->query->get('nonce'),
84 | $this->request->query->get('echostr'),
85 | $plainText
86 | );
87 |
88 | return $plainText;
89 | }
90 |
91 | /**
92 | * @param ReplyMessageInterface $replyMessage
93 | * @return string
94 | */
95 | public function reply(ReplyMessageInterface $replyMessage): string
96 | {
97 | if ($this->request->query->has('echostr')) {
98 | return $this->decryptEchoStr();
99 | } else {
100 | return $this->encryptReply($this->buildReply($replyMessage));
101 | }
102 | }
103 |
104 | /**
105 | * @param string $reply
106 | * @return string
107 | */
108 | private function encryptReply(string $reply): string
109 | {
110 | $cipherText = '';
111 |
112 | $this->crypt->EncryptMsg(
113 | $reply,
114 | $this->request->query->get('timestamp'),
115 | $this->request->query->get('nonce'),
116 | $cipherText
117 | );
118 |
119 | return $cipherText;
120 | }
121 |
122 | /**
123 | * @param ReplyMessageInterface $replyMessage
124 | * @return string
125 | */
126 | private function buildReply(ReplyMessageInterface $replyMessage): string
127 | {
128 | $reply = $replyMessage->formatForReply();
129 |
130 | $reply['ToUserName'] = $this->request->attributes->get('FromUserName');
131 | $reply['FromUserName'] = $this->request->attributes->get('ToUserName');
132 | $reply['CreateTime'] = (int)$this->request->attributes->get('CreateTime');
133 |
134 | $element = new SimpleXMLElement('');
135 |
136 | $this->arrayToXml($reply, $element);
137 |
138 | $dom = dom_import_simplexml($element);
139 |
140 | return $dom->ownerDocument->saveXML($dom->ownerDocument->documentElement);
141 | }
142 |
143 | /**
144 | * @param array $data
145 | * @param SimpleXMLElement $element
146 | * @return void
147 | */
148 | private function arrayToXml(array $data, SimpleXMLElement &$element): void
149 | {
150 | foreach ($data as $key => $value) {
151 | if (is_numeric($key)) {
152 | $key = 'item';
153 | }
154 |
155 | if (is_array($value)) {
156 | $subNode = $element->addChild($key);
157 | $this->arrayToXml($value, $subNode);
158 | } elseif (is_string($value)) {
159 | $node = dom_import_simplexml($element->addChild($key));
160 | $no = $node->ownerDocument;
161 | $node->appendChild($no->createCDATASection($value));
162 | } else {
163 | $element->addChild($key, $value);
164 | }
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/Crypt/WXBizMsgCrypt.php:
--------------------------------------------------------------------------------
1 | m_sToken = $token;
35 | $this->m_sEncodingAesKey = $encodingAesKey;
36 | $this->m_sCorpid = $Corpid;
37 | }
38 |
39 | /**
40 | * 验证URL
41 | *
42 | * @param string $sMsgSignature 签名串,对应URL参数的msg_signature
43 | * @param string $sTimeStamp 时间戳,对应URL参数的timestamp
44 | * @param string $sNonce 随机串,对应URL参数的nonce
45 | * @param string $sEchoStr 随机串,对应URL参数的echostr
46 | * @param string $sReplyEchoStr 解密之后的echostr,当return返回0时有效
47 | * @return int 成功0,失败返回对应的错误码
48 | */
49 | public function VerifyURL($sMsgSignature, $sTimeStamp, $sNonce, $sEchoStr, &$sReplyEchoStr)
50 | {
51 | if (strlen($this->m_sEncodingAesKey) != 43) {
52 | return ErrorCode::$IllegalAesKey;
53 | }
54 |
55 | $pc = new PrpCrypt($this->m_sEncodingAesKey);
56 | //verify msg_signature
57 | $sha1 = new SHA1;
58 | $array = $sha1->getSHA1($this->m_sToken, $sTimeStamp, $sNonce, $sEchoStr);
59 | $ret = $array[0];
60 |
61 | if ($ret != 0) {
62 | return $ret;
63 | }
64 |
65 | $signature = $array[1];
66 | if ($signature != $sMsgSignature) {
67 | return ErrorCode::$ValidateSignatureError;
68 | }
69 |
70 | $result = $pc->decrypt($sEchoStr, $this->m_sCorpid);
71 | if ($result[0] != 0) {
72 | return $result[0];
73 | }
74 | $sReplyEchoStr = $result[1];
75 |
76 | return ErrorCode::$OK;
77 | }
78 |
79 | /**
80 | * 将公众平台回复用户的消息加密打包.
81 | *
82 | * - 对要发送的消息进行AES-CBC加密
83 | * - 生成安全签名
84 | * - 将消息密文和安全签名打包成xml格式
85 | *
86 | *
87 | * @param string $sReplyMsg 公众平台待回复用户的消息,xml格式的字符串
88 | * @param string $sTimeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp
89 | * @param string $sNonce 随机串,可以自己生成,也可以用URL参数的nonce
90 | * @param string &$sEncryptMsg 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串,
91 | * 当return返回0时有效
92 | *
93 | * @return int 成功0,失败返回对应的错误码
94 | */
95 | public function EncryptMsg($sReplyMsg, $sTimeStamp, $sNonce, &$sEncryptMsg)
96 | {
97 | $pc = new PrpCrypt($this->m_sEncodingAesKey);
98 |
99 | //加密
100 | $array = $pc->encrypt($sReplyMsg, $this->m_sCorpid);
101 | $ret = $array[0];
102 | if ($ret != 0) {
103 | return $ret;
104 | }
105 |
106 | if ($sTimeStamp == null) {
107 | $sTimeStamp = time();
108 | }
109 | $encrypt = $array[1];
110 |
111 | //生成安全签名
112 | $sha1 = new SHA1;
113 | $array = $sha1->getSHA1($this->m_sToken, $sTimeStamp, $sNonce, $encrypt);
114 | $ret = $array[0];
115 | if ($ret != 0) {
116 | return $ret;
117 | }
118 | $signature = $array[1];
119 |
120 | //生成发送的xml
121 | $xmlparse = new XMLParse;
122 | $sEncryptMsg = $xmlparse->generate($encrypt, $signature, $sTimeStamp, $sNonce);
123 | return ErrorCode::$OK;
124 | }
125 |
126 |
127 | /**
128 | * 检验消息的真实性,并且获取解密后的明文.
129 | *
130 | * - 利用收到的密文生成安全签名,进行签名验证
131 | * - 若验证通过,则提取xml中的加密消息
132 | * - 对消息进行解密
133 | *
134 | *
135 | * @param string $sMsgSignature 签名串,对应URL参数的msg_signature
136 | * @param string $sTimeStamp 时间戳 对应URL参数的timestamp
137 | * @param string $sNonce 随机串,对应URL参数的nonce
138 | * @param string $sPostData 密文,对应POST请求的数据
139 | * @param string &$sMsg 解密后的原文,当return返回0时有效
140 | *
141 | * @return int 成功0,失败返回对应的错误码
142 | */
143 | public function DecryptMsg($sMsgSignature, $sTimeStamp, $sNonce, $sPostData, &$sMsg)
144 | {
145 | if (strlen($this->m_sEncodingAesKey) != 43) {
146 | return ErrorCode::$IllegalAesKey;
147 | }
148 |
149 | $pc = new PrpCrypt($this->m_sEncodingAesKey);
150 |
151 | //提取密文
152 | $xmlparse = new XMLParse;
153 | $array = $xmlparse->extract($sPostData);
154 | $ret = $array[0];
155 |
156 | if ($ret != 0) {
157 | return $ret;
158 | }
159 |
160 | if ($sTimeStamp == null) {
161 | $sTimeStamp = time();
162 | }
163 |
164 | $encrypt = $array[1];
165 |
166 | //验证安全签名
167 | $sha1 = new SHA1;
168 | $array = $sha1->getSHA1($this->m_sToken, $sTimeStamp, $sNonce, $encrypt);
169 | $ret = $array[0];
170 |
171 | if ($ret != 0) {
172 | return $ret;
173 | }
174 |
175 | $signature = $array[1];
176 | if ($signature != $sMsgSignature) {
177 | return ErrorCode::$ValidateSignatureError;
178 | }
179 |
180 | $result = $pc->decrypt($encrypt, $this->m_sCorpid);
181 | if ($result[0] != 0) {
182 | return $result[0];
183 | }
184 | $sMsg = $result[1];
185 |
186 | return ErrorCode::$OK;
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/tests/Api/UserTest.php:
--------------------------------------------------------------------------------
1 | httpClient = \Mockery::mock(HttpClientInterface::class);
30 |
31 | $this->user = new User();
32 | }
33 |
34 | /**
35 | * @return void
36 | */
37 | public function testCreate()
38 | {
39 | $this->httpClient->shouldReceive('postJson')
40 | ->once()
41 | ->with('user/create', ['foo' => 'bar'])
42 | ->andReturn(['errcode' => 0]);
43 |
44 | $this->user->setHttpClient($this->httpClient);
45 |
46 | $this->assertEquals(['errcode' => 0], $this->user->create(['foo' => 'bar']));
47 | }
48 |
49 | /**
50 | * @return void
51 | */
52 | public function testGet()
53 | {
54 | $this->httpClient->shouldReceive('get')
55 | ->once()
56 | ->with('user/get', ['userid' => 'foo'])
57 | ->andReturn(['errcode' => 0]);
58 |
59 | $this->user->setHttpClient($this->httpClient);
60 |
61 | $this->assertEquals(['errcode' => 0], $this->user->get('foo'));
62 | }
63 |
64 | /**
65 | * @return void
66 | */
67 | public function testUpdate()
68 | {
69 | $this->httpClient->shouldReceive('postJson')
70 | ->once()
71 | ->with('user/update', ['foo' => 'bar'])
72 | ->andReturn(['errcode' => 0]);
73 |
74 | $this->user->setHttpClient($this->httpClient);
75 |
76 | $this->assertEquals(['errcode' => 0], $this->user->update(['foo' => 'bar']));
77 | }
78 |
79 | /**
80 | * @return void
81 | */
82 | public function testDelete()
83 | {
84 | $this->httpClient->shouldReceive('get')
85 | ->once()
86 | ->with('user/delete', ['userid' => 'foo'])
87 | ->andReturn(['errcode' => 0]);
88 |
89 | $this->user->setHttpClient($this->httpClient);
90 |
91 | $this->assertEquals(['errcode' => 0], $this->user->delete('foo'));
92 | }
93 |
94 | /**
95 | * @return void
96 | */
97 | public function testBatchDelete()
98 | {
99 | $this->httpClient->shouldReceive('postJson')
100 | ->once()
101 | ->with('user/batchdelete', ['useridlist' => ['foo', 'bar']])
102 | ->andReturn(['errcode' => 0]);
103 |
104 | $this->user->setHttpClient($this->httpClient);
105 |
106 | $this->assertEquals(['errcode' => 0], $this->user->batchDelete(['foo', 'bar']));
107 | }
108 |
109 | /**
110 | * @param int $departmentId
111 | * @param bool $fetchChild
112 | * @param bool $needDetail
113 | * @param string $uri
114 | * @param array $query
115 | * @return void
116 | *
117 | * @dataProvider listProvider
118 | */
119 | public function testList($departmentId, $fetchChild, $needDetail, $uri, $query)
120 | {
121 | $this->httpClient->shouldReceive('get')
122 | ->once()
123 | ->with($uri, $query)
124 | ->andReturn(['errcode' => 0]);
125 |
126 | $this->user->setHttpClient($this->httpClient);
127 |
128 | $this->assertEquals(['errcode' => 0], $this->user->list($departmentId, $fetchChild, $needDetail));
129 | }
130 |
131 | /**
132 | * @return array
133 | */
134 | public function listProvider()
135 | {
136 | return [
137 | [1, false, false, 'user/simplelist', ['department_id' => 1, 'fetch_child' => 0]],
138 | [2, false, true, 'user/list', ['department_id' => 2, 'fetch_child' => 0]],
139 | [3, true, false, 'user/simplelist', ['department_id' => 3, 'fetch_child' => 1]],
140 | [4, true, true, 'user/list', ['department_id' => 4, 'fetch_child' => 1]]
141 | ];
142 | }
143 |
144 | /**
145 | * @return void
146 | */
147 | public function testConvertIdToOpenid()
148 | {
149 | $this->httpClient->shouldReceive('postJson')
150 | ->once()
151 | ->with('user/convert_to_openid', ['userid' => 'foo'])
152 | ->andReturn(['errcode' => 0]);
153 |
154 | $this->user->setHttpClient($this->httpClient);
155 |
156 | $this->assertEquals(['errcode' => 0], $this->user->convertIdToOpenid('foo'));
157 | }
158 |
159 | /**
160 | * @return void
161 | */
162 | public function testConvertOpenidToUserId()
163 | {
164 | $this->httpClient->shouldReceive('postJson')
165 | ->once()
166 | ->with('user/convert_to_userid', ['openid' => 'foo'])
167 | ->andReturn(['errcode' => 0]);
168 |
169 | $this->user->setHttpClient($this->httpClient);
170 |
171 | $this->assertEquals(['errcode' => 0], $this->user->convertOpenidToUserId('foo'));
172 | }
173 |
174 | /**
175 | * @return void
176 | */
177 | public function testAuthSuccess()
178 | {
179 | $this->httpClient->shouldReceive('get')
180 | ->once()
181 | ->with('user/authsucc', ['userid' => 'foo'])
182 | ->andReturn(['errcode' => 0]);
183 |
184 | $this->user->setHttpClient($this->httpClient);
185 |
186 | $this->assertEquals(['errcode' => 0], $this->user->authSuccess('foo'));
187 | }
188 |
189 | /**
190 | * @return void
191 | */
192 | public function testGetInfo()
193 | {
194 | $this->httpClient->shouldReceive('get')
195 | ->once()
196 | ->with('user/getuserinfo', ['code' => 'foo'])
197 | ->andReturn(['errcode' => 0]);
198 |
199 | $this->user->setHttpClient($this->httpClient);
200 |
201 | $this->assertEquals(['errcode' => 0], $this->user->getInfo('foo'));
202 | }
203 |
204 | /**
205 | * @return void
206 | */
207 | public function testGetDetail()
208 | {
209 | $this->httpClient->shouldReceive('postJson')
210 | ->once()
211 | ->with('user/getuserdetail', ['user_ticket' => 'foo'])
212 | ->andReturn(['errcode' => 0]);
213 |
214 | $this->user->setHttpClient($this->httpClient);
215 |
216 | $this->assertEquals(['errcode' => 0], $this->user->getDetail('foo'));
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/src/App.php:
--------------------------------------------------------------------------------
1 | Api\Agent::class,
35 | 'appChat' => Api\AppChat::class,
36 | 'batch' => Api\Batch::class,
37 | 'checkIn' => Api\CheckIn::class,
38 | 'corp' => Api\Corp::class,
39 | 'crm' => Api\CRM::class,
40 | 'department' => Api\Department::class,
41 | 'invoice' => Api\Invoice::class,
42 | 'media' => Api\Media::class,
43 | 'menu' => Api\Menu::class,
44 | 'message' => Api\Message::class,
45 | 'tag' => Api\Tag::class,
46 | 'user' => Api\User::class,
47 | ];
48 |
49 | /**
50 | * @param array $config
51 | */
52 | public function __construct(array $config)
53 | {
54 | parent::__construct();
55 |
56 | $this->config = new ArrayCollection($config);
57 |
58 | $this->registerServices();
59 | }
60 |
61 | /**
62 | * @return void
63 | */
64 | private function registerServices(): void
65 | {
66 | $this->registerLogger();
67 | $this->registerHttpClient();
68 | $this->registerCache();
69 | $this->registerToken();
70 | $this->registerCallback();
71 | $this->registerHttpClientWithToken();
72 |
73 | foreach ($this->apiServices as $id => $class) {
74 | $this->registerApi($id, $class);
75 | }
76 |
77 | $this->registerJsApiTicket();
78 | $this->registerTicket();
79 | $this->registerJssdk();
80 | }
81 |
82 | /**
83 | * @return void
84 | */
85 | private function registerLogger(): void
86 | {
87 | $log = $this->config->get('log');
88 |
89 | if (is_subclass_of($log, LoggerInterface::class)) {
90 | $this->register('logger', $log);
91 | } elseif ($log) {
92 | $this->register('logger_handler', StreamHandler::class)
93 | ->setArguments([$log['file'], isset($log['level']) ? $log['level'] : 'debug']);
94 | $this->registerMonolog();
95 | } else {
96 | $this->register('logger_handler', NullHandler::class);
97 | $this->registerMonolog();
98 | }
99 | }
100 |
101 | /**
102 | * @return void
103 | */
104 | private function registerMonolog(): void
105 | {
106 | $this->register('logger', Logger::class)
107 | ->addArgument('WeWork')
108 | ->addMethodCall('setTimezone', [new \DateTimeZone('PRC')])
109 | ->addMethodCall('pushHandler', [new Reference('logger_handler')]);
110 | }
111 |
112 | /**
113 | * @return void
114 | */
115 | private function registerHttpClient(): void
116 | {
117 | $this->register('client', Client::class)
118 | ->addArgument(new Reference('logger'))
119 | ->setFactory([ClientFactory::class, 'create']);
120 |
121 | $this->register('http_client', HttpClient::class)
122 | ->addArgument(new Reference('client'));
123 | }
124 |
125 | /**
126 | * @return void
127 | */
128 | private function registerCache(): void
129 | {
130 | $cache = $this->config->get('cache');
131 |
132 | if (is_subclass_of($cache, CacheInterface::class)) {
133 | $this->register('cache', $cache);
134 | } else {
135 | $service = $this->register('cache', FilesystemCache::class);
136 |
137 | if ($cache && isset($cache['path'])) {
138 | $service->setArguments(['', 0, $cache['path']]);
139 | }
140 | }
141 | }
142 |
143 | /**
144 | * @return void
145 | */
146 | private function registerToken(): void
147 | {
148 | $this->register('token', Token::class)
149 | ->addMethodCall('setCorpId', [$this->config->get('corp_id')])
150 | ->addMethodCall('setSecret', [$this->config->get('secret')])
151 | ->addMethodCall('setCache', [new Reference('cache')])
152 | ->addMethodCall('setHttpClient', [new Reference('http_client')]);
153 | }
154 |
155 | /**
156 | * @return void
157 | */
158 | private function registerCallback(): void
159 | {
160 | $this->register('request', Request::class)
161 | ->setFactory([Request::class, 'createFromGlobals']);
162 |
163 | $this->register('crypt', WXBizMsgCrypt::class)
164 | ->setArguments([$this->config->get('token'), $this->config->get('aes_key'), $this->config->get('corp_id')]);
165 |
166 | $this->register('callback', Callback::class)
167 | ->setArguments([new Reference('request'), new Reference('crypt')]);
168 | }
169 |
170 | /**
171 | * @return void
172 | */
173 | private function registerHttpClientWithToken(): void
174 | {
175 | $this->register('client_with_token', Client::class)
176 | ->setArguments([new Reference('logger'), new Reference('token')])
177 | ->setFactory([ClientFactory::class, 'create']);
178 |
179 | $this->register('http_client_with_token', HttpClient::class)
180 | ->addArgument(new Reference('client_with_token'));
181 | }
182 |
183 | /**
184 | * @param string $id
185 | * @param string $class
186 | *
187 | * @return void
188 | */
189 | private function registerApi(string $id, string $class): void
190 | {
191 | $api = $this->register($id, $class)
192 | ->addMethodCall('setHttpClient', [new Reference('http_client_with_token')]);
193 |
194 | if (in_array($id, ['agent', 'menu', 'message'])) {
195 | $api->addMethodCall('setAgentId', [$this->config->get('agent_id')]);
196 | }
197 | }
198 |
199 | /**
200 | * @return void
201 | */
202 | private function registerJsApiTicket(): void
203 | {
204 | $this->register('jsApiTicket', JsApiTicket::class)
205 | ->addMethodCall('setSecret', [$this->config->get('secret')])
206 | ->addMethodCall('setCache', [new Reference('cache')])
207 | ->addMethodCall('setHttpClient', [new Reference('http_client_with_token')]);
208 | }
209 |
210 | /**
211 | * @return void
212 | */
213 | private function registerTicket(): void
214 | {
215 | $this->register('ticket', Ticket::class)
216 | ->addMethodCall('setCache', [new Reference('cache')])
217 | ->addMethodCall('setHttpClient', [new Reference('http_client_with_token')]);
218 | }
219 |
220 | /**
221 | * @return void
222 | */
223 | private function registerJssdk(): void
224 | {
225 | $this->register('jssdk', JSSdk::class)
226 | ->addMethodCall('setCorpId', [$this->config->get('corp_id')])
227 | ->addMethodCall('setJsApiTicket', [new Reference('jsApiTicket')])
228 | ->addMethodCall('setTicket', [new Reference('ticket')]);
229 | }
230 | }
231 |
--------------------------------------------------------------------------------