ufee/amoapi

amoCRM API 客户端 (https://www.amocrm.ru/developers/content/platform/abilities/)

0.9.4.1 2024-07-11 12:39 UTC

README

amoCRM 工作客户端
从版本 0.9.0.0 开始支持 oAuth 认证

安装

composer require ufee/amoapi

运行测试

编辑 vendor/ufee/amoapi/tests/Config.php

composer require --dev phpunit/phpunit ^5
vendor/bin/phpunit vendor/ufee/amoapi

通过 oAuth 初始化客户端

OAuth 令牌可以存储在多种方式中

文件存储

默认使用 (/vendor/ufee/amoapi/src/Cache/),也可以指定自己的路径
创建子目录:{path}/{domain}/{client_id}.json
强烈建议使用自己的路径进行缓存,否则数据将在更新到新版本时被 composer 删除。

\Ufee\Amo\Oauthapi::setOauthStorage(
	new \Ufee\Amo\Base\Storage\Oauth\FileStorage(['path' => '/full/oauth/path'])
);

Redis

支持库 phpredis
键格式:{domain}_{client_id}

$redis = new \Redis();
$redis->connect('/var/run/redis/redis.sock');
$redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP);
$redis->select(5); // switch to specific db

\Ufee\Amo\Oauthapi::setOauthStorage(
	new \Ufee\Amo\Base\Storage\Oauth\RedisStorage(['connection' => $redis])
);

MongoDB

支持库 mongodb

$mongo = new \MongoDB\Client('mongodb://host');
$collection = $mongo->selectCollection('database', 'some_collection');

\Ufee\Amo\Oauthapi::setOauthStorage(
	new \Ufee\Amo\Base\Storage\Oauth\MongoDbStorage(['collection' => $collection])
);

存储继承

实现一个继承 \Ufee\Amo\Base\Storage\Oauth\AbstractStorage 的类

class CustomOauthStorage extends \Ufee\Amo\Base\Storage\Oauth\AbstractStorage
{
	protected function initClient(Oauthapi $client) {
		parent::initClient($client);
		$key = $client->getAuth('domain').'_'.$client->getAuth('client_id');
		if ($data = getClientOauthData($key)) {
			static::$_oauth[$key] = $data;
		}
	}
	public function setOauthData(Oauthapi $client, array $oauth) {
		parent::setOauthData($client, $oauth);
		$key = $client->getAuth('domain').'_'.$client->getAuth('client_id');
		return setClientOauthData($key, $oauth);
	}
}
\Ufee\Amo\Oauthapi::setOauthStorage(
	new CustomOauthStorage()
);

第三方存储

库不进行 OAuth 数据的缓存

\Ufee\Amo\Oauthapi::setOauthStorage(
	new \Ufee\Amo\Base\Storage\Oauth\AbstractStorage()
);
$amo = \Ufee\Amo\Oauthapi::setInstance([...]);
$first_token = $amo->fetchAccessToken($code);
saveToCustomStorage($first_token);
$last_saved_token = getFromCustomStorage();
$amo->setOauth($last_saved_token);
$amo->onAccessTokenRefresh(function($oauth) {
	saveToCustomStorage($oauth);
});

弃用

指定 OAuth 数据的存储路径(已弃用方法)

$amo->setOauthPath('path_to/oauth');
// equal to new method
\Ufee\Amo\Oauthapi::setOauthStorage(
    new \Ufee\Amo\Base\Storage\Oauth\FileStorage(['path' => 'path_to/oauth'])
);

获取用于特定账户的对象

$amo = \Ufee\Amo\Oauthapi::setInstance([
    'domain' => 'testdomain',
    'client_id' => 'b6cf0658-b19...', // id приложения
    'client_secret' => 'D546t4yRlOprfZ...',
    'redirect_uri' => 'https://site.ru/amocrm/oauth/redirect',
    'zone' => 'ru', // or com
    'timezone' => 'Europe/Moscow',
    'lang' => 'ru', // or en
    'user_agent' => '' // ifempty = Amoapi v.<version> (<client_id>)
]);
$amo->setAuth('user_agent', 'MyCustomUserAgent');

根据应用程序 id 获取先前初始化的对象

$amo = \Ufee\Amo\Oauthapi::getInstance('b6cf0658-b19...');

获取 amoCRM 应用程序的授权 URL
用于提取授权码

$first_auth_url = $amo->getOauthUrl($arg = ['mode' => 'popup', 'state' => 'amoapi']);

通过授权码一次性获取 access_token 和 refresh_token
根据所选存储方式缓存 OAuth 数据
在 API 请求中自动应用

$oauth = $amo->fetchAccessToken($code);

在需要的情况下,可以手动强制设置 OAuth 数据
数据也将根据所选存储方式自动缓存

$amo->setOauth([
	'token_type' => 'Bearer',
	'expires_in' => 86400,
	'access_token' => 'bKSuyc4u6oi...',
	'refresh_token' => 'a89iHvS9uR4...',
	'created_at' => 1597678161
]);

如果 refresh_token 的有效期未到,则自动更新 access_token
在需要的情况下,可以手动强制通过 refresh_token 更新 OAuth 数据
新的 OAuth 数据也将自动缓存

$oauth = $amo->refreshAccessToken($refresh_token = null); // при передаче null используются кешированные oauth данные

在自动更新 access_token 时调用回调函数

$amo->onAccessTokenRefresh(function($oauth, $query, $response) {
	echo $query->startDate().' - ['.$query->method.'] '.$query->getUrl()."\n";
	print_r($query->post_data);
	echo $query->endDate().' - ['.$response->getCode().'] '. $response->getData(). "\n\n";
	echo "\nRefreshed oauth: \n";
	print_r($oauth);
});
$amo->onAccessTokenRefreshError(function($exc, $query, $response) {
	echo $query->startDate().' - ['.$query->method.'] '.$query->getUrl()."\n";
	print_r($query->post_data);
	echo $query->endDate().' - ['.$response->getCode().'] '. $response->getData(). "\n\n";
	exit('Error refresh token: '.$exc->getMessage().', code: '.$exc->getCode());
});

在首次执行 fetchAccessToken() 方法后,可以像平常一样使用客户端
重复执行 fetchAccessToken() 或 setOauth() 方法仅在有以下情况时必要:

  1. 应用程序中的访问密钥已更改
  2. amoCRM 账户的子域已更改
  3. refresh_token 已过期
  4. 收到授权错误

建议确保缓存文件夹 - /vendor/ufee/amoapi/src/Cache/ - 没有公开访问 - (如果使用默认目录)

将 API 密钥交换为 OAuth 授权码

$resp_code = $amo->ajax()->exchangeApiKey(
	$crm_login, 
	$api_hash, 
	$client_id, 
	$client_secret
);
if ($resp_code === 202) {
	// Запрос принят, код авторизации будет отправлен на redirect_uri
}

通过 API-hash 初始化客户端

获取用于特定账户的对象

$amo = \Ufee\Amo\Amoapi::setInstance([
    'id' => 123,
    'domain' => 'testdomain',
    'login' => 'test@login',
    'hash' => 'testhash',
    'zone' => 'com', // default: ru
    'timezone' => 'Europe/London', // default: Europe/Moscow
    'lang' => 'en' // default: ru
]);

在 401 错误时启用/禁用自动授权
会话(cookie)缓存到文件中

$amo->autoAuth(true); // true/false, рекомендуется true

客户端操作

启用日志记录(Logs/m-Y/domain.log)

$amo->queries->logs(true); // to default path

$amo->queries->logs('path_to_log/queries'); // to custom path

在指定的时间内不超过 1 个请求,单位为秒

$amo->queries->setDelay(0.5); // default: 0.15 sec

/api/v2/account 请求在文件中缓存,时间以秒为单位

\Ufee\Amo\Services\Account::setCacheTime(1800); // default: 600 sec

通过引入 oAuth 引入用户请求调试(更新)

$amo->queries->onResponseCode(429, function(\Ufee\Amo\Base\Models\QueryModel $query) {
	echo 'Resp code 429, retry '.$query->retries."\n";
});
$amo->queries->listen(function(\Ufee\Amo\Base\Models\QueryModel $query) {
    $code = $query->response->getCode();
    echo $query->startDate().' - ['.$query->method.'] '.$query->getUrl()."\n";
    print_r($query->headers);
    if ($code === 0) {
        echo $query->response->getError()."\n\n";
    } else {
        print_r(count($query->json_data) ? $query->json_data : $query->post_data);
        echo $query->endDate().' - ['.$query->response->getCode().'] '.$query->response->getData()."\n\n";
    }
});

请求缓存

缓存存储支持多种方式

文件存储

默认使用 (/vendor/ufee/amoapi/src/Cache/),也可以指定自己的路径
创建缓存文件:{path}/{domain}_{hash}.cache
强烈建议使用自己的路径进行缓存,否则数据将在更新到新版本时被 composer 删除。

$apiClient->queries->setCacheStorage(
	new \Ufee\Amo\Base\Storage\Query\FileStorage($apiClient, ['path' => 'pull_path_to/cache'])
);

Redis

支持库 phpredis
键格式:{domain}-cache:{hash}

$redis = new \Redis();
$redis->connect('/var/run/redis/redis.sock');
$redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP); // \Redis::SERIALIZER_IGBINARY recommended
$redis->select(5); // switch to specific db

$apiClient->queries->setCacheStorage(
	new \Ufee\Amo\Base\Storage\Query\RedisStorage($apiClient, ['connection' => $redis])
);

实体搜索

通过附加字段搜索

$leads = $amo->leads()->searchByCustomField('Москва', 'Город', 300); // by CF name, max 300
$leads = $amo->leads()->searchByCustomField('Москва', 623425); // by CF id
$companies = $amo->companies()->searchByName('ООО Шарики за Ролики', 100); // by name, max 100
$contacts = $amo->contacts()->searchByEmail('Test@Mail.Ru', 0); // by email, no max limit
$contacts = $amo->contacts()->searchByPhone('89271002030');

处理附加字段

移除值

$entity->cf('Имя поля')->reset();
$entity->cf('Организация')->removeBy('name', 'ИП Петров А.А.');

获取值

$entity->cf('Имя поля')->getValue();
$entity->cf('Имя поля')->getValues();
$entity->cf('Имя поля')->getEnums();
$entity->cf('Дата')->format('Y-m-d');
$entity->cf('Дата')->getTimestamp();
$entity->cf('Организация')->getValues();

设置值

$entity->cf('Имя поля')->setEnum($enum);
$entity->cf('Имя поля')->setEnums($enums);
$entity->cf('Число')->setValue(5);
$entity->cf('Текст')->setValue('Test');
$entity->cf('Мультисписок')->reset()->setValues(['Мужская одежда', 'Аксессуары']);
$entity->cf('День рождения')->setDate('Y-m-d');
$entity->cf('Дата')->setTimestamp(14867456357);
$entity->cf('Дата')->setDate('Y-m-d');
$entity->cf('Переключатель')->enable();
$entity->cf('Переключатель')->disable();
$entity->cf('Полный адрес')->setCountry('Россия');
$entity->cf('Полный адрес')->setRegion('Чувашская республика');
$entity->cf('Полный адрес')->setCity('Чебоксары');
$entity->cf('Полный адрес')->setIndex(428000);
$entity->cf('Телефон')->setValue('987654321', 'Home');
$entity->cf('Телефон')->setValue('123456789');
$entity->cf('Email')->setValue('best@list.ru');
$entity->cf('Мгн. сообщения')->setValue('bestJa', 'Jabber');
$entity->cf('Юр. лицо')->setName('Команда F5');
$entity->cf('Юр. лицо')->setAddress('РФ, ЧР, г.Чебоксары');
$entity->cf('Юр. лицо')->setType(1);
$entity->cf('Юр. лицо')->setInn(123);
$entity->cf('Юр. лицо')->setKpp(456);
$entity->cf('Организация')->addValue([
    'name' => 'ИП Петров А.А.',
    'city' => 'Москва',
    '...' => '...'
]);

集合操作

遍历、搜索和过滤

foreach ($amo->leads as $lead) { ... }
$amo->leads->each(function(&$lead) { ... });
$leadsByCf = $amo->leads->find('name', 'Трубы гофрированные');
$leadsBySale = $amo->leads->filter(function($lead) {
    return $lead->sale > 0;
});
$firstLead = $lead->first();
$lastLead = $lead->last();

排序

$leads->sortBy('sale', 'DESC');
$leads->usort(function($a, $b) {});
$leads->uasort(function($a, $b) {});

其他

$has_contains =  $leads->contains('name', 'Test');
$sale_sum = $leads->sum('sale');
$leads = $leads->transform(function($lead) {
    return [
        'id' => $lead->id,
        'name' => $lead->name
    ];
});
$leads_array = $leads->toArray();

交易操作

获取所有交易

$leads = $amo->leads;
$leads = $amo->leads()->recursiveCall();
$leads = $amo->leads()->call(); // первые 500

根据最后修改日期获取

$leads = $amo->leads()
             ->modifiedFrom('Y-m-5 09:20:00') // по дате, с 5 числа текущего месяца, с 9:20 утра
             ->modifiedFrom(1528188143) // или по timestamp
             ->maxRows(1000)
             ->listing();

通过 ID 获取

$lead = $amo->leads()->find($id); // array|integer

根据附加条件获取交易

$lead = $amo->leads()->where('key', $val)->recursiveCall();

交易相关实体

$contact = $lead->contact;
$contacts = $lead->contacts;
$company = $lead->company;
$tasks = $lead->tasks;
$notes = $lead->notes;

创建交易

$leads = [
    $amo->leads()->create(),
    $amo->leads()->create()
];
$leads[0]->name = 'Amoapi v7 - 1';
$leads[1]->name = 'Amoapi v7 - 2';
$amo->leads()->add($leads);

$lead = $amo->leads()->create();
$lead->name = 'Amoapi v7';
$lead->attachTag('Amoapi');
$lead->pipeline_id = $amo->account->pipelines->main();
$lead->status_id = $lead->pipeline->statuses->first();
$lead->responsible_user_id = $amo->account->currentUser->id;
$lead->sale = 100500;
$lead->cf('Число')->setValue(5);
$lead->cf('Текст')->setValue('Test');
$lead->cf('Мультисписок')->reset()->setValues(['Мужская одежда', 'Аксессуары']);
$lead->cf('Дата')->setValue(date('Y-m-d'));
$lead->cf('Переключатель')->disable();
$lead->save();

从联系人创建交易

$lead = $contact->createLead();
$lead->name = 'Amoapi v7';
$lead->save();

复制交易

$copy = clone $lead;
$copy->name = 'New lead';
$copy->save();

处理联系人

获取所有联系人

$contacts = $amo->contacts;
$contacts = $amo->contacts()->recursiveCall();
$contacts = $amo->contacts()->call(); // первые 500

通过 ID 获取

$contact = $amo->contacts()->find($id); // array|integer

根据附加条件获取联系人

$contacts = $amo->contacts()->where('key', $val)->recursiveCall();

联系人相关实体

$leads = $contact->leads;
$company = $contact->company;
$tasks = $lead->tasks;
$notes = $lead->notes;

创建联系人

$contacts = [
    $amo->contacts()->create(),
    $amo->contacts()->create()
];
$contacts[0]->name = 'Amoapi v7 - 1';
$contacts[1]->name = 'Amoapi v7 - 2';
$amo->contacts()->add($contacts);

$contact = $amo->contacts()->create();
$contact->name = 'Amoapi v7';
$contact->attachTags(['Amoapi', 'Test']);
$contact->cf('Телефон')->setValue('987654321', 'Home');
$contact->cf('Телефон')->setValue('123456789');
$contact->cf('Email')->setValue('best@list.ru');
$contact->save();

从交易创建联系人

$contact = $lead->createContact();
$contact->name = 'Amoapi v7';
$contact->save();

复制联系人

$copy = clone $contact;
$copy->name = 'New contact';
$copy->save();

处理公司

获取所有公司

$companies = $amo->companies;
$companies = $amo->companies()->recursiveCall();
$companies = $amo->companies()->call(); // первые 500

通过 ID 获取

$company = $amo->companies()->find($id); // array|integer

根据附加条件获取公司

$companies = $amo->companies()->where('key', $val)->recursiveCall();

公司相关实体

$leads = $company->leads;
$contacts = $company->contacts;
$tasks = $lead->tasks;
$notes = $lead->notes;

创建公司

$companys = [
    $amo->companies()->create(),
    $amo->companies()->create()
];
$companys[0]->name = 'Amoapi v7 - 1';
$companys[1]->name = 'Amoapi v7 - 2';
$amo->companies()->add($companys);

$company = $amo->companies()->create();
$company->name = 'Amoapi v7';
$company->save();

从联系人或交易创建公司

$company = $contact->createCompany();
$company = $lead->createCompany();
$company->name = 'Amoapi v7';
$company->save();

复制公司

$copy = clone $company;
$copy->name = 'New company';
$copy->save();

处理任务

获取所有任务

$tasks = $amo->tasks;
$tasks = $amo->tasks()->recursiveCall();
$tasks = $amo->tasks()->call(); // первые 500

通过 ID 获取

$task = $amo->tasks()->find($id); // array|integer

根据附加条件获取任务

$tasks = $amo->tasks()->where('key', $val)->recursiveCall();

创建任务

$tasks = [
    $amo->tasks()->create(),
    $amo->tasks()->create()
];
$tasks[0]->text = 'Amoapi v7 - 1';
$tasks[0]->element_type = 3;
$tasks[0]->element_id = 34762721;
$tasks[1]->text = 'Amoapi v7 - 2';
$tasks[1]->element_type = 2;
$tasks[1]->element_id = 34762720;
$amo->tasks()->add($tasks);

$task = $amo->tasks()->create();
$task->text = 'Amoapi v7';
$task->element_type = 1;
$task->element_id = 34762725;
$task->save();

从联系人、交易或公司创建任务

$task = $contact->createTask($type = 1);
$task = $lead->createTask($type = 1);
$task = $company->createTask($type = 1);
$task->text = 'Amoapi v7';
$task->element_type = 1;
$task->element_id = 34762725;
$task->save();

获取父级联系人、交易或公司

$contact = $task->linkedContact;
$lead = $task->linkedLead;
$comapny = $task->linkedCompany;

处理备注

获取所有备注

$notes = $amo->notes;
$notes = $amo->notes()->where('type', 'contact')->recursiveCall();
$notes = $amo->notes()->where('type', 'lead')->call(); // первые 500

根据ID和实体类型获取备注

$note = $amo->notes()->find($id, 'lead');

根据附加条件获取备注

$notes = $amo->notes()->where('key', $val)->recursiveCall();

创建备注

$notes = [
    $amo->notes()->create(), 
    $amo->notes()->create()
];
$notes[0]->note_type = 4;
$notes[0]->text = 'Amoapi v7 - 1';
$notes[0]->element_type = 3;
$notes[0]->element_id = 34762721;
$notes[1]->note_type = 4;
$notes[1]->text = 'Amoapi v7 - 2';
$notes[1]->element_type = 2;
$notes[1]->element_id = 34762720;
$amo->notes()->add($notes);

$note = $amo->notes()->create();
$note->note_type = 4;
$note->text = 'Amoapi v7';
$note->element_type = 1;
$note->element_id = 34762725;
$note->save();

从联系人、交易或公司创建备注

$note = $contact->createNote($type = 4);
$note = $lead->createNote($type = 4);
$note = $company->createNote($type = 4);
$note->text = 'Amoapi v7';
$note->element_type = 2;
$note->element_id = 34762728;
$note->save();

固定/解除固定备注(备注类型4)

$note->setPinned(true); // true/false

获取文件内容(备注类型5)

$contents = $note->getAttachment();

获取父级联系人、交易或公司

$contact = $note->linkedContact;
$lead = $note->linkedLead;
$comapny = $note->linkedCompany;

处理列表

获取所有列表(目录)

$catalogs = $amo->catalogs;

通过 ID 获取

$catalog = $amo->catalogs()->find($id); // array|integer

根据附加条件获取列表

$catalogs = $amo->catalogs()->where('key', $val)->call();

列表相关实体

$elements = $catalog->elements;

创建列表

$catalogs = [
    $amo->catalogs()->create(),
    $amo->catalogs()->create()
];
$catalogs[0]->name = 'Amoapi v7 - 1';
$catalogs[1]->name = 'Amoapi v7 - 2';
$amo->catalogs()->add($catalogs);

$catalog = $amo->catalogs()->create();
$catalog->name = 'Amoapi v7';
$catalog->save();

删除列表

$amo->catalogs()->delete($catalogs); // array|integer
$catalog->delete();

处理目录元素(商品)

获取商品

$element = $amo->catalogElements()->find($id);
$elements = $amo->catalogElements()->where('catalog_id', 1234)->call();
$elements = $catalog->elements;

添加商品

$element = $amo->catalogElements()->create();
$element->catalog_id = 1234;

$element = $catalog->createElement();
$element->name = 'Холодильник LG';
$element->cf('Артикул')->setValue('ML-4675');
$element->cf('Количество')->setValue(100);
$element->cf('Цена')->setValue(38500);
$element->save();

更新商品

$element->cf('Скидка')->setValue(5);
$element->save();

商品相关实体

$catalog = $element->catalog;
$leads = $element->leads;

删除商品

$amo->elements()->delete($elements); // array|integer
$catalog->elements->delete(); // удаление всех товаров каталога
$element->delete();

将商品附加到交易

$lead->attachElement($catalog->id, $element->id, $count = 1);

处理买家

获取所有买家

$customers = $amo->customers;
$customers = $amo->customers()->recursiveCall();
$customers = $amo->customers()->call(); // первые 500

通过 ID 获取

$customer = $amo->customers()->find($id); // array|integer

根据附加条件获取买家

$customer = $amo->customers()->where('key', $val)->recursiveCall();

买家相关实体

$contact = $customer->contact;
$contacts = $customer->contacts;
$company = $customer->company;
$tasks = $customer->tasks;
$notes = $customer->notes;
$transactions = $customer->transactions;

创建买家

$customer = $amo->customers()->create();
$customer->name = 'Amoapi v7';
$customer->next_date = time();
$customer->next_price = 100;
$customer->responsible_user_id = $amo->account->currentUser->id;
$customer->cf('Число')->setValue(5);
$customer->cf('Текст')->setValue('Test');
$customer->cf('Мультисписок')->reset()->setValues(['Мужская одежда', 'Аксессуары']);
$customer->cf('Дата')->setValue(date('Y-m-d'));
$customer->cf('Переключатель')->disable();
$customer->save();

从联系人创建买家

$customer = $contact->createCustomer();
$customer->name = 'Amoapi v7';
$customer->next_date = time();
$customer->save();

删除买家

$amo->customers()->delete($customers); // array|integer
$customer->delete();

处理购买

获取交易(购买)

$transactions = $amo->transactions;
$transactions = $customer->transactions;

添加交易

$transaction = $amo->transactions()->create();
$transaction->customer_id = 1234;

$transaction = $customer->createTransaction();
$transaction->price = 1500;
$transaction->save();

更新买家交易评论

$transaction->comment = 'Тест';
$transaction->save();

删除买家交易

$amo->transactions()->delete($transactions); // array|integer
$customer->transactions->delete(); // удаление всех покупок покупателя
$transaction->delete(); // удаление покупки

处理前端方法

获取webhooks

$webhooks = $amo->webhooks;

添加webhooks

$result = $amo->webhooks()->subscribe('http://site.ru/handler/', ['add_lead', 'update_contact', 'responsible_lead']);

删除webhooks

$result = $amo->webhooks()->unsubscribe('http://site.ru/handler/', ['update_contact', 'responsible_lead']);

处理前端方法

从备注下载文件

$contents = $amo->ajax()->getAttachment('AbCd_attach_name.zip');

执行任意查询

$amo->ajax()->get($url = '/ajax/example', $args = []);
$amo->ajax()->post($url = '/ajax/example', $data = [], $args = [], $post_type = 'raw OR json');
$amo->ajax()->postJson($url = '/ajax/example', $data = [], $args = []);
$amo->ajax()->patch($url = '/ajax/example', $data = [], $args = []);

处理Salesbot

$bots = $amo->salesbots()->get($page = 1, $limit = 250);
$bots = $amo->salesbots;

$start = $amo->salesbots()->start($bot_id, $entity_id, $entity_type = 2);
$stop = $amo->salesbots()->stop($bot_id, $entity_id, $entity_type = 2);