В чём проблема с моим запросом cURL?

Привет! Я пытаюсь написать запрос cURL для своего сайта на WordPress, чтобы интегрировать Discourse с подписками WooCommerce. Вот моя функция, которая, похоже, не работает. Не упустил ли я что-то?

function execute_discourse_curl_request($subscription) {

    $subscription_status = $subscription->get_status();

    $user_id = $subscription->get_user_id();
    $user = get_user_by('ID', $user_id);
    $username = $user->user_login;

    $request = "";

    if ($subscription_status == 'active') {
        $request = "PUT";
    }

    if ($subscription_status == 'cancelled' || $subscription_status == 'on-hold') {
        $request = "DELETE";
    }

    $api_key = get_option( 'discourse_custom_api_key' );

    $group_id = 41;
    $api_url = 'https://forum.example.com/groups/' . $group_id . '/members.json';
    
    $data = [
        'usernames' => [$username],
    ];
    
    $curl = curl_init();
    curl_setopt_array($curl, [
        CURLOPT_URL => $api_url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_ENCODING => "",
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
        CURLOPT_CUSTOMREQUEST => $request,
        CURLOPT_POSTFIELDS => json_encode($data),
        CURLOPT_HTTPHEADER => [
            "Api-Key: " . $api_key,
            "Api-Username: system",
            "Content-Type: application/json"
        ],
    ]);
    
    $response = curl_exec($curl);
    $err = curl_error($curl);
    
    curl_close($curl);

    if ($err) {
        $to = 'hello@example.com';
        $subject = 'Ошибка в запросе cURL для обновления пользователя Discourse';
        $message = 'Произошла ошибка';
        wp_mail($to, $subject, $message);
    }
}

Здравствуйте,

Какую ошибку вы получаете? Что возвращает ответ?
Это поможет понять проблему.

Проблема в том, что я не получаю ни ошибки, ни подтверждения. Похоже, cURL просто не работает. Я вижу, что при выполнении cURL использовался API-ключ Discourse, так что какая-то часть работает, но пользователь не добавляется и не удаляется из группы, как задумано. При этом я могу подтвердить, что все переменные указаны верно.

При успехе или неудаче должен возвращаться payload.

Проверили ли вы содержимое $response?

Кроме того, я считаю, что формат параметра usernames должен быть строкой вида username1,username2,..., а не иначе.

Если я не делаю чего-то неправильно, то запрос к `“/groups/${group_id}/members.json”" кажется довольно неочевидным. Кроме того, он, похоже, не задокументирован и его нельзя увидеть во вкладке Network браузера при отправке формы «Добавить пользователя» на странице группы в Discourse. Опять же, возможно, я что-то делаю не так.

В любом случае, путь для запроса — /groups/${group_id}/members.json, где числовой ID группы подставляется вместо group_id. Тело запроса требует параметра group_id, но этот параметр должен быть установлен в значение name группы. Таким образом, аргументы выглядят примерно так:

$group_id = 45;
$group_name = 'publishers';
$args     = array(
    'method' => 'DELETE', // или 'PUT'
	'body'   => array(
		// 'group_id'  => $group_name, edit: этот параметр не нужен, не помню, что именно я проверял при тестировании.
		'usernames' => 'sally,Ben',
	)
);

// путь:
/groups/${group_id}/members.json

Если на вашем сайте WordPress установлен плагин WP Discourse, вы можете использовать его статическую вспомогательную функцию discourse_request, чтобы избежать использования curl. Обратите внимание, что я сослался на статическую функцию в файле plugin-utilities, но пространство имён, которое нужно использовать для внешних запросов, находится здесь: https://github.com/discourse/wp-discourse/blob/main/lib/utilities.php.

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

function zalg_add_users_to_group() {
	$group_id = 45;
    $group_name = 'publishers';
    $method = 'PUT'; // 'PUT' для добавления пользователей; 'DELETE' для удаления
	$args     = array(
		'method' => $method,
		'body'   => array(
			// 'group_id'  => $group_name, edit: этот параметр не нужен
			'usernames' => 'sally,Ben',
		)
	);
	$response = DiscourseUtilities::discourse_request( "/groups/${group_id}/members.json", $args );
    // верните, запишите в лог или обработайте ответ
}

Если WordPress используется как провайдер SSO для Discourse, существует вспомогательная функция, позволяющая добавить одного пользователя в одну или несколько групп:

function zalg_sso_add_users_to_group() {
    $user_id = 1; // ID пользователя в WordPress
    $group_names = 'foo,bar,baz'; // одно или несколько названий групп, разделённых запятыми, без пробелов после запятых!
    DiscourseUtilities::add_user_to_discourse_group($user_id, $group_names);
}

Чтобы добавить нескольких пользователей в одну группу, вам всё равно придётся использовать метод discourse_request, показанный в первом примере кода.

Ответ — просто ошибка 500 :frowning:

Это героическая помощь, спасибо, Саймон! Это было бы намного проще, если бы я мог использовать существующие вспомогательные функции в плагине WP Discourse. Я использую свой сайт WordPress для SSO.

Однако в моём случае при вызове функции всё ещё возникает критическая ошибка. Не понимаю, почему. То, что вы говорите, немного выше моего уровня понимания, но вот что я использую в своём functions.php:

<?php

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

add_action('woocommerce_subscription_status_cancelled', 'zalg_sso_add_users_to_group', 10, 3);

function zalg_sso_add_users_to_group($subscription) {
    $user_id = $subscription->get_user_id();
    $group_names = 'premium';
    DiscourseUtilities::remove_user_to_discourse_group($user_id, $group_names);
}

Не знаю, что я упускаю?

Функция для удаления пользователя из одной или нескольких групп называется remove_user_from_discourse_group, а вы назвали её remove_user_to_discourse_group.

Функции для добавления и удаления пользователя из одной или нескольких групп находятся здесь: wp-discourse/lib/utilities.php at 99325e15190f3a705284dbf582f1c4b2c0b21492 · discourse/wp-discourse · GitHub.

Вы правы! Глупая ошибка. Спасибо за помощь, Саймон. Жаль, что нет ящика для чаевых. Дайте знать, если я смогу чем-то помочь вам. До встречи!

Чтобы продолжить, вы знаете, где можно найти вспомогательные функции для более детального редактирования профиля пользователя? Было бы здорово, если бы можно было установить фотографию профиля и биографию пользователя, если это возможно.

В файле Utilities есть вспомогательная функция для этого:

DiscourseUtilities::sync_sso_record( $sso_params );

Вчера я опубликовал пример того, как её использовать: I cannot add user to the discouse forum from a wordpress website when user added in a membership - #10 by simon. Самая сложная часть — создать массив для аргумента sso_params. Этот массив должен содержать поле external_id. Оно устанавливается в значение ID пользователя WordPress:

	$sso_params = array(
		'external_id' => $user_id,
	);

В полезную нагрузку можно включить любые поля из этого списка: discourse/lib/discourse_connect_base.rb at 8f52fd1051e20fdff41321c5cff99fda05af86c1 · discourse/discourse · GitHub. Обратите внимание на массив BOOLS, который показан сразу под списком ACCESSORS, на который я дал ссылку. Он указывает, какие из возможных параметров являются булевыми (true/false). При отправке запросов из WordPress любые булевы поля должны задаваться строками 'true' или 'false', а не значениями PHP true или false.

Для обновления аватара установите поле avatar_url в массиве $sso_params, а также установите поле avatar_force_update в значение 'true'.

Существует поле bio, которое можно использовать для установки биографии.

Плагин WP Discourse уже устанавливает поля bio и avatar_url. У него также есть настройка «Принудительное обновление аватара» в настройках SSO. Проблема может заключаться в том, что эти настройки обновляются только тогда, когда пользователь входит в Discourse через сайт WordPress. Функция sync_sso_record обновляет поля немедленно, без необходимости входа пользователя в Discourse.

Кроме того, если вы обнаружите, что биографии пользователей не устанавливаются в Discourse, возможно, они не задаются на вашем сайте WordPress, откуда плагин ожидает их получить: wp-discourse/lib/plugin-utilities.php at 99325e15190f3a705284dbf582f1c4b2c0b21492 · discourse/wp-discourse · GitHub. Если в WooCommerce используется другое поле для биографии, вы можете получить доступ к этому полю и использовать его при вызове sync_sso_record.