反馈是任何成功的产品开发过程的基石。 对于 WordPress 插件开发人员来说,了解用户停用插件的原因可以提供宝贵的见解。

当我使用 Cloudflare 为 WordPress 开发另一个很棒的插件时,如果用户停用我的插件,我想要一个反馈系统。 我开始在网上搜索,找到的解决方案对我来说不可行。

许多 WordPress 插件创建者依靠内置的停用调查或第三方服务来收集反馈。 然而,这些方法存在局限性和问题:

  • 成本:运行服务器和管理反馈收集基础设施可能会很昂贵。 因此,第三方提供商收取相当多的费用。
  • 延迟:缓慢的反馈机制可能会阻止用户参与。 当用户停用插件时,他预计 10 秒内不会加载页面。
  • 集成麻烦:许多反馈系统需要陡峭的学习曲线或复杂的集成过程,例如 SDK 等。
  • 隐私问题:大多数第三方服务提供商都会收集电子邮件地址等敏感数据。 将用户敏感数据存储在可能会也可能不会出售这些数据、遭到黑客攻击或更糟的平台上; 就是不可行。

那么,如何才能高效、安全且经济高效地收集反馈呢? 根据 Cloudflare WorkersKV 和 [D1](https://developers.cloudflare.com/d1/ )。

我决定基于令人惊叹的 Cloudflare Workers 平台创建自己的 REST API,有效地将使用 Pages 部署的会员区域与 API 互连。 您可以在此处 详细了解我如何构建会员区域。

这意味着我可以将 API 使用限制为尊贵的会员,同时提供存储数据的隐私和安全性。 该 API 在全球范围内运行速度极快,停机时间为零,同时保持低成本和增强的安全性。 人们还想要什么? 😃

这仅仅是开始,因为我已经为未来的服务准备了几个端点,包括免费的和付费的给客户、开发人员和合作伙伴。

入门

我的 API 入门非常简单。 注册我的会员专区 。 注册后,只需访问您的个人资料 。 如果您已经有一个帐户,则会动态生成一组 API 令牌。

您将在此页面上找到:帐户 ID公共令牌私人令牌

  • 账户ID用于识别您的账户并存储相应信息。
  • 公共代币仅限于某些操作,请参阅范围。
  • 私人代币不像公共代币那样受到限制,您可以使用它来读取、写入、删除您帐户上的反馈。 再次,请查看范围。

注意:我的 API 有严格的速率限制以防止滥用。 如果你玩得太多,你可能会受到至少一小时或更长时间的限制。

使用 WordPress 反馈 API 端点是免费的,这是完全免费的,所以请不要尝试滥用它,否则我将不得不禁止您的帐户。

将 API 与您的插件集成

集成非常简单,您只需按照以下步骤操作:

  1. 连接到 admin_footer 并注入您的反馈模式/表单。
  2. 使用“admin_enqueue_scripts”加载 CSS 和 JS; 确保您只在插件页面上加载它们。 提示:创建您自己的自定义样式,否则您将破坏插件页面样式,甚至与其他插件反馈模式发生冲突。
  3. 创建一个管理AJAX函数来处理反馈; 例如wp_ajax_your_plugin_deactivation_feedback。 注意:始终使用随机数来保护您的 AJAX 功能。

在收集反馈时,您需要收集一些信息并将其相应地发送到API。 由于我非常关心隐私,API 不会存储电子邮件地址或任何其他被视为机密的内容。

此外,为了尽可能遵守 GDPR,我提供了 D1 提供的所有五个位置来存储收集的数据:

  • 北美西部
  • 北美东部
  • 西欧
  • 东欧洲
  • 亚太地区

考虑到这一点,我还提供了一个 GEO 辅助函数,它返回您的托管区域。 这意味着您可以自动将数据存储在托管 WordPress 的封闭区域中。

获取区域的示例函数:

 1private function get_region($account, $token)
 2{
 3	$api_url = "https://api.mecanik.dev/v1/developer/$account/geo-info";
 4
 5	$response = wp_remote_post($api_url, [
 6		'headers' => [
 7			'Authorization' => 'Bearer '.$token,
 8			'Content-Type' => 'application/json; charset=utf-8',
 9		],
10		'method' => 'GET',
11	]);
12
13	$response_body = wp_remote_retrieve_body($response);
14	$response_data = json_decode($response_body, true);
15	
16	return $response_data;
17}

这将返回一个 JSON 响应,例如:

 1{
 2    "longitude": "8.68370",
 3    "latitude": "50.11690",
 4    "continent": "EU",
 5    "asn": 14061,
 6    "asOrganization": "Digital Ocean",
 7    "country": "DE",
 8    "isEUCountry": "1",
 9    "city": "Frankfurt am Main",
10    "region": "Hesse",
11    "timezone": "Europe/Berlin",
12    "postalCode": "60341",
13    "recommended-storage-region": "weur"
14}

正如您在此示例中看到的,服务器驻留在“Hesse”区域,因此 API 建议将数据存储在“weur”区域。 对此持保留态度; API 不能保证它总是返回正确的“建议”。

现在,您可以继续创建反馈对象并向 API 发送以下内容:reasoncommentswp_plugin_namewp_plugin_versionwp_site_urlwp_versionwp_localewp_multisite php_versiondb_typedb_versionserver_typeserver_versiondate_created

发送反馈的示例函数:

 1public function wp_ajax_your_plugin_deactivation_feedback()
 2{
 3	if (!wp_verify_nonce($_POST['nonce'], 'wp_ajax_your_plugin_deactivation_feedback_nonce')) {
 4		wp_send_json_error(esc_html__('Request is invalid. Please refresh the page and try again.', 'your-plugin-name'), 400, 0);
 5		exit();
 6	}
 7	
 8	$account = "....";
 9	$token = "....";
10	$api_url = "https://api.mecanik.dev/v1/developer/$account/wp-feedback";
11
12	$region = $this->get_region($account, $token);
13
14	$response = wp_remote_post($api_url, [
15		'headers' => [
16			'Authorization' => 'Bearer '.$token,
17			'Content-Type' => 'application/json; charset=utf-8',
18		],
19		'body' => json_encode([
20			'reason'             => sanitize_text_field($_POST['reason']),
21			'comments'           => sanitize_textarea_field($_POST['comments']),
22			'wp_plugin_name'     => $this->plugin_name,
23			'wp_plugin_version'  => $this->version,
24			'wp_site_url'        => get_bloginfo('url'),
25			'wp_version'         => $this->get_wp_version(),
26			'wp_locale'          => get_locale(),
27			'wp_multisite'       => is_multisite(),
28			'php_version'        => $this->get_php_version(),
29			'db_type'            => $this->get_db_type(),
30			'db_version'         => $this->get_db_version(),
31			'server_type'        => $this->get_server_type(),
32			'server_version'     => $this->get_server_version(),
33			'date_created'       => current_time('mysql'),
34			'region'             => $region['recommended-storage-region'],
35		]),
36		'method' => 'PUT',
37		'data_format' => 'body'
38	]);
39
40	$response_body = wp_remote_retrieve_body($response);
41	$response_data = json_decode($response_body);
42
43	if ($response_data && isset($response_data->success) && $response_data->success === false) {
44		$error_message = isset($response_data->errors[0]->message) ? esc_html($response_data->errors[0]->message) : 'Unknown error';
45		wp_send_json_error($error_message, 400);
46		exit();
47	}
48	else if ($response_data && isset($response_data->success) && $response_data->success === true && isset($response_data->error)) {
49		wp_send_json_error($response_data->error, 400);
50		exit();
51	}
52	
53	wp_send_json_success($response_data, 200, 0);
54}

将返回成功保存的反馈响应,如下所示:反馈已保存在 <region_name> 区域。

在上面的示例函数中,您可以看到我们收集了 php_versiondb_typedb_version 等。为了您的方便,我将提供一些示例函数:

 1private function get_wp_version() {
 2	global $wp_version;
 3	return $wp_version;
 4}
 5
 6private function get_php_version() {
 7	return PHP_VERSION;
 8}
 9
10private function get_db_type() {
11	global $wpdb;
12
13	$query = "SELECT TABLE_SCHEMA FROM information_schema.TABLES WHERE TABLE_NAME = 'wp_options'";
14	$result = $wpdb->get_row($query);
15
16	if ($result && isset($result->TABLE_SCHEMA)) {
17		$table_schema = $result->TABLE_SCHEMA;
18
19		if (strpos($table_schema, 'mysql') !== false) {
20			return 'MySQL';
21		} elseif (strpos($table_schema, 'pgsql') !== false) {
22			return 'PostgreSQL';
23		} elseif (strpos($table_schema, 'sqlite') !== false) {
24			return 'SQLite';
25		}
26	}
27
28	return 'Unknown';
29}
30
31private function get_db_version() {
32	global $wpdb;
33	return $wpdb->db_version();
34}
35
36private function get_server_type() {
37	if (isset($_SERVER['SERVER_SOFTWARE'])) {
38		return explode(' ', $_SERVER['SERVER_SOFTWARE'])[0];
39	}
40	return 'Unknown';
41}
42
43private function get_server_version() {
44	$uname = php_uname('s');
45
46	$serverTypes = [
47		'Apache' => 'Apache',
48		'Nginx' => 'Nginx',
49		'LiteSpeed' => 'LiteSpeed'
50	];
51
52	if (isset($serverTypes[$uname])) {
53		return $serverTypes[$uname];
54	}
55
56	return '0.0.0';
57}

现在您应该拥有收集 WordPress 插件停用反馈所需的一切。 根据需要进行修改以满足您的需求。

开始收集停用反馈

如果您已按照上述步骤操作,当用户尝试停用您的插件时,应该会出现反馈模式:

WordPress 插件停用反馈

用户可以输入停用您的插件的原因。 您可以随时从 API 检索所有收集的反馈。

API 文档

基本端点:

https://api.mecanik.dev/v1/developer

WordPress 反馈端点:

https://api.mecanik.dev/v1/developer/{account_id}/wp-feedback

获取评论

  • 方法: GET
  • 链接: https://api.mecanik.dev/v1/developer/{account_id}/wp-feedback?region=[region]
  • 链接参数:
    • region (必须): 存储评论的地区代码。可以是 `wnam`, `enam`, `weur`, `eeur` 或 `apac`。
  • 范围: read:wp_feedback
  • 头部:
    • Authorization: 访问验证令牌。
  • 响应:
    • 200 OK 如果请求成功并返回评论数据。
    • 400 错误请求 如果区域无效。
    • 403 禁止 如果令牌未被授权。
    • 404 未找到 如果令牌不存在。
    • 406 不接受 如果提交了无效的区域。

保存评论

  • 方法: PUT
  • 链接: https://api.mecanik.dev/v1/developer/{account_id}/wp-feedback
  • 范围: write:wp_feedback
  • 头部:
    • Authorization: 访问验证令牌。
    • Content-Type: application/json
  • 内容:
    • 包含以下内容的JSON对象:
      • reason (必须): 用户禁用的原因。接受的格式:`string`。
      • comments (必须): 用户禁用的评论。接受的格式:`string`。
      • wp_plugin_name (必须): 当前的Wordpress插件名称。接受的格式:`string`。
      • wp_plugin_version (必须): 当前的Wordpress插件版本。接受的格式:`0.0.0.0`。
      • wp_site_url (必须): 当前的Wordpress网址。接受的格式:`string`。
      • wp_version (必须): 当前的Wordpress版本。接受的格式:`0.0.0.0`。
      • wp_locale (必须): 当前的Wordpress地区设置。接受的格式:`string`。
      • wp_multisite (必须): 当前的Wordpress是否为多站点安装。接受的格式:`boolean`。
      • php_version (必须): 当前的PHP版本。接受的格式:`0.0.0.0`。
      • db_type (必须): 当前的数据库服务器类型。接受的格式:`string`。
      • db_version (必须): 当前的数据库服务器版本。接受的格式:`0.0.0.0`。
      • server_type (必须): 当前的web服务器类型。接受的格式:`string`。
      • server_version (必须): 当前的web服务器版本。接受的格式:`0.0.0.0`。
      • date_created (必须): 评论创建的日期。接受的格式:`YYYY-MM-DD HH:MM:SS`。
      • region (必填): 反饋所存儲的地區代碼。可以是 `wnam`, `enam`, `weur`, `eeur`, 或 `apac`。
  • 响应:
    • 200 OK 如果评论成功保存。
    • 400 错误请求 如果提供的数据有误。
    • 403 禁止 如果令牌未被授权。
    • 404 未找到 如果令牌不存在。
    • 500 内部服务器错误 如果保存评论时出现错误。

删除评论

  • 方法: DELETE
  • 链接: https://api.mecanik.dev/v1/developer/{account_id}/wp-feedback
  • 范围: write:wp_feedback
  • 头部:
    • Authorization: 访问验证令牌。
  • 内容:
    • 包含以下内容的JSON对象:
      • feedback_ids (必需): 整数数组。 示例:`[1,2,3]`
      • region (必需): 存储反馈的区域代码。 它可以是 `wnam`、`enam`、`weur`、`eeur` 或 `apac`。
  • 响应:
    • 200 OK 如果评论成功删除。
    • 400 错误请求 如果提供的评论ID无效。
    • 403 禁止 如果令牌未被授权或已被撤销/封禁。
    • 404 未找到 如果令牌不存在。
    • 500 内部服务器错误 如果删除评论时出现错误。

结论

拥有这样的反馈系统将对你的插件开发产生巨大的影响。 我希望这篇文章对您有所帮助,并且您可以使用我的 API 改进您的 WordPress 插件。

我将来可能会引入激活反馈,但我对此非常怀疑,因为用户无法“选择加入”或“选择退出”它。 因此,目前我们只会收到停用反馈。

如果您有任何反馈或建议,请随时使用下面的评论系统。