Проблема
В процессе публикации своего приложения в RuStore, я столкнулся с проблемой обновления сборки, первоначально-установленной из Google Play. Дело в том, что несколько лет назад, когда я размещал приложение в Google Play, я доверил управление ключами подписи Google App Signing. Это значит, что мой локальный ключ используется в качестве "загрузочного", а пользователи на устройства получают уже сборки, подписанные внутренним ключом Google. Прямого доступа к этому ключу нет, и, соответственно, я не могу подписать им сборку для RuStore для бесшовного обновления приложения.
В результате, RuStore предлагает удалить старое приложение и установить новую версию с нуля:

Это ведет ко множеству неудобств - от необходимости повторной авторизации в лучшем случае, до потери локальной базы данных и настроек пользователя в худшем.
Решение 1 - частичное
В Google Play мы можем отключить систему подписи Google App Signing, загрузить свой ключ и частично решить этим проблему, но только для новых пользователей - старые будут продолжать получать обновления из Google Play, подписанные их внутренним ключом, до полной переустановки приложения.

Решение 2 - радикальное
Мы можем разделить версии приложения путем изменения applicationId или добавления к нему суффикса, например, .rustore. В таком случае, установка из RuStore пройдет гладко, но приложение задублируется - на устройстве будет как версия из Google Play, так и из RuStore.
Это может быть удобным, если в приложении хранятся какие-либо данные локально и вы боитесь их потерять при полной переустановке. В новой версии можно добавить краткую инструкцию по ручной миграции, если таковая имеется (например, описание способа сделать бэкап файла базы данных, настроек, и их восстановления).
Решение 3 - продолжать использовать внутреннюю подпись Google Play
Но как? У нас же нет прямого доступа к ключу!
Мы можем продолжать использовать подпись Google App Signing для сборок RuStore и других магазинов. Для этого необходимо в консоли настроить внутреннее тестирование и создать новый выпуск:

Подписываем релизный APK сборки для RuStore локально своим ключом. Загружаем в консоль и нажимаем "Сохранить как проект". Остальные поля заполнять необязательно.

После этого, переходим в App Bundle Explorer и открываем последнюю загруженную версия приложения:

Переходим в таб "Скачанные файлы" и нажимаем на кнопку загрузки APK распространения:

Далее нужно почистить хвосты. Удаляем выпуск из внутреннего тестирования и после этого можно удалить APK из App Bundle Explorer:


Все, подписанный APK сборки для магазина RuStore ключом Google App Signing готов. Предварительно советую проверить накат обновления путем ручной установки сборки поверх версии, установленной из Google Play - все должно пройти без ошибок и с сохранением пользовательских данных предыдущей установки.
Таким образом, мы смогли решить проблему подписи приложения, при этом наша сборка не отправляется на проверку в Google, так как мы ее не публикуем, а используем как черновик. Следовательно, Google нас не поругает за использование внутри сторонних решений биллинга и других запрещенных библиотек.
Проверял на сборке с интегрированным биллингом RuStore. В Huawei Store пока не публиковался, поэтому реакцию Google на HMS сервисы в сборках для внутреннего тестирования проверить не могу. Если кто-нибудь попробует, пишите, добавлю в статью.
Автор: Виталий Симонов