Удаление пользовательских данных🔗
Согласно закону GDPR, пользователь может потребовать удалить свои пользовательские данные из систем. Для того чтобы сделать это, у пользователя есть несколько способов: Удаление данных через окно поддержки или Удаление данных по кнопке.
Процедура удаления данных пользователя🔗
При любом подходе (через окно виджета поддержки, или при нажатии кнопки), когда созданный запрос (тикет) поступает в работу, выполняются следующие шаги:
- Вручную проверяются данные о пользователе на основе данных поддержки и GMTool'ов проекта, чтобы исключить злоумышленное удаление ПД (лично ли он делал запрос, действительно ли этого хотел, и др)
- Операторы поддержки вручную удаляют/блокируют игроков в GMTool проекта (или проектов, если запрашивалось удаление аккаунта MyGames)
- Операторы поддержки заводят задачи на проект по полному удалению пользователя
- После успешного удаления пользователя, данные удаляются из используемых сторонних системах (MRGS, 1Link) посредством постановки задач
Более подробно про каждый шаг и развернутое описание логики удаления можно посмотреть в документации MyGames Support
Удаление данных через виджет поддержки🔗
Если игрок хочет удалить свои данные, ему необходимо создать запрос из соответствующей статьи на сайте поддержки или в мобильном виджете. Пример статьи.
После этого сотрудники поддержки связываются с пользователем, уточняют все детали, и начинают процесс удаления, описанный выше
Удаление данных по кнопке🔗
В соответствии с требованием AppStore, с февраля 2022г. на проектах, где есть создание аккаунта, пользователь должен иметь возможность инициировать процесс удаления своего аккаунта через простое нажатие кнопки в интерфейсе приложения.
Доступ к такому функционалу может иметь ряд негативных последствий как для пользователя, так и для проекта - случайное нажатие, нажатие другим человеком, соответственно крайне желательно предусмотреть возможность отмены решения об удалении со стороны пользователя. Срок, в течении которого пользователь может самостоятельно отменить запрос можно получить при создании тикета через API в поле cancel_to, или обратиться к контактному лицу службы поддержки. По истечению срока для самостоятельной отмены, аккаунт пользователя будет заблокирован вручную службой поддержки. После этого будет запущен внутренний процесс удаления аккаунта, описанный выше.
Рекомендации по реализации интерфейсных решений, удовлетворяющих требованию AppStore по предоставлению пользователям простого функционала удаления аккаунта.
- Нажатие кнопки удаления должно инициировать дополнительные предупреждения (например, «Вы уверены, что хотите удалить аккаунт, включая все достижения и весь прогресс?»).
- После активации кнопки удаления пользователю необходимо давать несколько дней для самостоятельной отмены процесса, например трансформируя кнопку удаления в кнопку отмены или введением дополнительного элемента интерфейса со схожей функцией.
- После нажатия кнопки удаления, в интерфейсе должна появиться заметная информация о том, что было запрошено удаление аккаунта. Это может быть реализовано через отображение поверх интерфейса плашки «Начато удаление аккаунта», через блокировку интерфейса, через появление дополнительного экрана с уведомлением перед запуском приложения.
- По истечению срока для самостоятельной отмены, пользователь будет заблокирован сотрудниками поддержки, и чтобы у пользователя не возникли вопросы о причинах блокировки, стоит предусмотреть специальное информационное окно с причиной блокировки, которое будет отображаться пользователю при попытке запустить приложение. Пример - «Вы запросили удаление аккаунта. Процесс удаления аккаунта занимает некоторое время. В течение этого периода аккаунт будет заблокирован.»
Серверное API MyGames Support для создания и отмены запросов на удаление данных юзера описано в документации MyGames Support, но мы рекомендуем использовать серверное и клиентское API MRGS, описанное ниже, поскольку:
- Вам не придется получать дополнительные секретные ключи, и скорее всего интеграция с MRGS у Вас уже настроена
- Наше API решает некоторые проблемы синхронизации отмены запросов между разными платформами, и другие возможные проблемы, подробнее смотрите ниже
Важное замечание про синхронизацию
Хотя требование об удалении аккаунта по кнопке выдвигается только на iOS, в случае, если приложение поддерживает синхронизацию на разных платформах или девайсах, то и статус удаления аккаунта тоже необходимо синхронизировать, чтобы пользователь везде видел, что его аккаунт в процессе удаления, и мог отменить запрос с любого из устройств.
Создание запроса на удаление🔗
Для создания запроса на удаление Вы можете использовать либо серверное, либо клиентское API:
При помощи серверного API
Для создания запроса на удаление воспользуйтесь API /support/deleteProfile/.
В ответ вы получите словарь, содержащий уникальный для всех проектов идентификатор тикета, время его создания, а также время, до которого пользователь может его отменить.
При помощи клиентского SDK
Чтобы запросить удаление данных пользователя, необходимо вызвать метод:
MRGSMyGamesSupport.Instance.RequestAccountDeletion((ticket, error) =>
{
if (error == null)
{
Debug.Log("MRGSMyGamesSupport#RequestAccountDeletion success: " + ticket);
}
else
{
if (error.Code == (int)MRGSMyGamesSupportError.NetworkError)
{
Debug.Log("Network error, try again later");
}
else
{
Debug.Log("MRGSMyGamesSupport#RequestAccountDeletion failed: " + error.Description);
}
}
});
// Async/await way
var (ticket, error) = await MRGSMyGamesSupport.Instance.RequestAccountDeletionAsync();
var ticket = await MRGSMyGamesSupport.Instance.RequestAccountDeletionAsync().Throwable();
[[MRGSMyGamesSupport sharedInstance] requestAccountDeletionWithCompletionHandler:^(MRGSMyGamesSupportTicket * _Nullable ticket, NSError * _Nullable error) {
if(!error){
NSLog(@"Created request: %@", [ticket description]);
}else{
if(error.code == MRGSMyGamesSupportErrorNetworkConnectionFailed){
NSLog(@"Network error, try again later");
}else{
NSLog(@"Received error - Code: %lu; Description: %@", error.code, error.localizedDescription);
}
}
}];
import androidx.annotation.Nullable;
import games.my.mrgs.MRGSError;
import games.my.mrgs.support.MRGSMyGamesSupport;
import games.my.mrgs.support.MRGSMyGamesSupportTicket;
import games.my.mrgs.utils.optional.BiConsumer;
MRGSMyGamesSupport.getInstance().requestAccountDeletion(new BiConsumer<MRGSMyGamesSupportTicket, MRGSError>() {
@Override
public void accept(@Nullable MRGSMyGamesSupportTicket ticket, @Nullable MRGSError error) {
if (error == null){
Log.d("MRGSMyGamesSupport", "RequestAccountDeletion success: " + ticket);
} else {
if (error.getErrorCode() == MRGSMyGamesSupport.NETWORK_ERROR){
Log.d("MRGSMyGamesSupport", "Network error");
} else {
Log.d("MRGSMyGamesSupport", "RequestAccountDeletion failed: " + error.getErrorText());
}
}
}
});
В ответ вы получите объект MRGSMyGamesSupportTicket, содержащий уникальный для всех проектов идентификатор тикета (который важно сохранить на сервере проекта как доп подтверждение действия пользователя), время его создания, а также время, до которого пользователь может его отменить.
В случае ошибки (например, нет интернета, сервер MRGS или саппорта недоступен) рекомендуем показать соответствующее окно пользователю. Возможные коды ошибок и советы по их обработке приведены в разделе ниже.
Дублирование данных на сервере проекта
Поскольку клиент могут взломать, а процедура удаления аккаунта в определенной степени не обратима, очень важно нажатие кнопки на удаление и отмену удаления аккаунта продублировать логами в админки/ГМ-тулы, чтобы служба поддержки после получения тикета могла убедиться в админке/ГМ-туле, что такой процесс был действительно инициирован пользователем, а не подделан. Также желательно, чтобы у службы поддержки в ГМ-туле должна быть возможность выбрать причину блокировки, либо отдельная кнопка для блокировки на время удаления аккаунта, чтобы пользователь в интерфейсе видел причину блокировки (см. рекомендации по реализации интерфейса выше).
Почему просто не хранить все данные на серверах MRGS и не вынуждать проекты хранить это на своих серверах?
Как правило, игры/приложения имеют своего рода систему авторизации пользователя, чтобы защитить от кражи аккаунта. Поэтому, все логируемые действия таких юзеров считаются более достоверными. MRGS не имеет систему авторизации пользователей, поэтому если хранить такие данные, на пример информацию о том что юзер запросил удаление, на стороне MRGS, нету ни какой возможности хоть как-то провалидировать этого пользователя, что это был действительно он. Поэтому подобные данные нужно хранить на серверах проектов, и именно на эти данные будет опираться служба поддержки при помощи GMTool проекта.
Что происходит после нажатия на кнопку?
После создания запроса на удаление через API, создается невидимый для пользователя тикет, который помещается в очередь на X дней (про количество дней смотрите в начале раздела. Сам тикет при этом виден сотрудникам поддержки и может быть найден через ticket search, но не берется в работу), в течении которых пользователь может его отменить, после этого времени тикет автоматически удаляется из очереди и переходит в статус "открытый", после этого его берут в работу сотрудники поддержки, и удаление аккаунта становится необратимым.
Отмена запроса на удаление🔗
В течении X дней запрос на удаление данных может быть отменен, величину этого интервала Вы получаете при создании тикета.
Нюансы отмены запроса
В случае, если приложение поддерживает синхронизацию на разных платформах или девайсах, то пользователь может отменить запрос с другого девайса/платформы. Например, инициация запроса идет с проекта iOS, но отменить юзер может это с Android. В таком случае при создании запроса использовался один projectId (iOS) а для удаления другой (Android), и для системы поддержки это два несвязанных проекта со своими userId. Поэтому при использовании API MyGamesSupport напрямую, Вам придется отменять запрос по каждому из проектов в цикле. При использовании MRGS такого делать не придется (см. ниже).
Для отмены запроса на удаление Вы можете использовать либо серверное, либо клиентское API:
При помощи серверного API
Для создания запроса на удаление воспользуйтесь API /support/cancelProfileDeletionServer/.
Данное API позволяет произвести отмену либо по конкретному проекту, либо сразу по всей группе проектов(поле fullProjectGroup), если в проекте используются сквозные идентификаторы пользователей и есть синхронизация между платформами (см. уточнение по нюансам отмены запроса выше), а также позволяет исключить некоторые проекты при удалении по всей группе проектов(поле excludeProjectIds), в случае, если некоторые из них имеют несквозные идентификаторы пользователей.
Таким образом, сделав один вызов API Вы сможете быстро отменить запрос на удаление данных.
При помощи клиентского SDK
Чтобы отменить запрос на удаление данных, необходимо вызвать метод:
var ticketId = 123; // Received from server
MRGSMyGamesSupport.getInstance().CancelAccountDeletionRequest(ticketId, (error) =>
{
if (error == null)
{
Debug.Log("MRGSMyGamesSupport#CancelAccountDeletionRequest success.");
}
else
{
if (error.Code == (int)MRGSMyGamesSupportError.TicketNotFound)
{
Debug.Log("No ticket is found, UI can be reset to normal.");
}
else if (error.Code == (int)MRGSMyGamesSupportError.NetworkError)
{
Debug.Log("Network error, try again later");
}
else
{
Debug.Log("MRGSMyGamesSupport#CancelAccountDeletionRequest failed: " + error.Description);
}
}
});
// Async/await way
var error = await MRGSMyGamesSupport.Instance.CancelAccountDeletionRequestAsync(ticketId);
await MRGSMyGamesSupport.Instance.CancelAccountDeletionRequestAsync(ticketId).Throwable();
long ticketId = 123; // Received from server
[[MRGSMyGamesSupport sharedInstance] cancelAccountDeletionRequestForTicketId:ticketId withCompletionHandler:^(NSError * _Nullable error) {
if(!error){
NSLog(@"Cancelled request: %lu", ticketId);
}else{
if(error.code == MRGSMyGamesSupportErrorTicketNotFound){
NSLog(@"No ticket is found, UI can be reset to normal.");
}else if(error.code == MRGSMyGamesSupportErrorNetworkConnectionFailed){
NSLog(@"Network error, try again later");
}else{
NSLog(@"Received error - Code: %lu; Description: %@", error.code, error.localizedDescription);
}
}
}];
import games.my.mrgs.MRGSError;
import games.my.mrgs.support.MRGSMyGamesSupport;
import games.my.mrgs.utils.optional.Consumer;
final long ticketId = 123; // Received from server
MRGSMyGamesSupport.getInstance().cancelAccountDeletionRequest(ticketId, new Consumer<MRGSError>() {
@Override
public void accept(MRGSError error) {
if (error == null){
Log.d("MRGSMyGamesSupport", "Cancelled request: " + ticketId);
} else {
if (error.getErrorCode() == MRGSMyGamesSupport.TICKET_NOT_FOUND){
Log.d("MRGSMyGamesSupport", "No ticket is found");
} else if (error.getErrorCode() == MRGSMyGamesSupport.NETWORK_ERROR){
Log.d("MRGSMyGamesSupport", "Network error");
} else {
final String message = String.format("Received error - Code: %d; Description: %s", error.getErrorCode(), error.describeContents())
Log.d("MRGSMyGamesSupport", message);
}
}
}
});
Обратите внимание, что для отмены требуется идентификатор тикета, который был получен при его создании, и который необходимо хранить на сервере проекта, это гарантирует безопасность клиентского метода. Данный метод отменит изначальный запрос, даже если он вызван с другой платформы, то есть если создать запрос с iOS, получить идентификатор тикета, и вызвать данный метод отмены с Android (с другим MRGS appId), то запрос все равно будет отменен.
В случае ошибки (например, нет интернета, сервер MRGS или саппорта недоступен) рекомендуем показать соответствующее окно пользователю. Возможные коды ошибок и советы по их обработке приведены в разделе ниже.
Дублирование данных на сервере проекта
Напомним, что все данные об отмене запроса тоже желательно дублировать на стороне сервера проекта, чтобы было две точки, где хранятся данные о действиях пользователя для их проверки.
Коды ошибок клиента🔗
В случае ошибки (например, нет интернета, сервер MRGS или саппорта недоступен) рекомендуем показать соответствующее окно пользователю. Возможные коды ошибок:
| Ошибка | Описание | Код ошибки |
|---|---|---|
| User not set | Не выставлен пользователь в SDK MRGS | 1010 |
| Unknown | Неизвестная ошибка. Подробности можно найти в тексте ошибки. | 1020 |
| Invalid input params | Неверно переданы параметры (например, ticketId пустой) | 1021 |
| Network error | Ошибка сети | 1022 |
| Ticket not found | Указанный ticketId не найден в системе саппорта | 1023 |
Enum с кодами ошибок в IDE
typedef NS_ERROR_ENUM(kMRGSMyGamesSupportErrorDomain, MRGSMyGamesSupportErrorCode) {
MRGSMyGamesSupportErrorUnknown = 1020,
MRGSMyGamesSupportErrorUserNotSet = 1010,
MRGSMyGamesSupportErrorInvalidParams = 1021,
MRGSMyGamesSupportErrorNetworkConnectionFailed = 1022,
MRGSMyGamesSupportErrorTicketNotFound = 1023,
};
Если при запросе получена ошибка 1023(Ticket not found), это значит, что такого тикета нет на сервере сапорта, то есть он мог быть отменен пользователем, например, через сайт поддержки. В таком случае можно убирать надпись в UI о процессе удаления аккаунта и пускать пользователя в приложение.
Тестирование удаления данных по запросу пользователя🔗
При создании тикета через API, он будет виден сотрудниками поддержки и может быть найден через ticket search даже в очереди ожидания, нужно проверить, что при нажатии на кнопку он создается, а при отмене удаляется. Полную процедуру удаления можно протестировать с сотрудниками поддержки, запустив ее для тестового пользователя.
Удаление данных о принятии GDPR🔗
После того как удаляется аккаунт пользователя и его данные, а клиент игры создает пользователю новый аккаунт, необходимо снова показать пользователю экран с принятием условий GDPR. Если вы используете MRGSGDPR модуль, то достаточно просто сбросить его в изначальное состояние. Подробней смотри Сброс настроек.
Дата создания: 2022-01-24