Обработка ошибок в Capacitor SDK
Каждая ошибка, возвращаемая SDK, является экземпляром AdaptyError. Пример:
Включите подробные логи перед отладкой. Большинство AdaptyError оборачивают исходную ошибку StoreKit, Play Billing, сети или бэкенда. При включённых подробных логах (adapty.setLogLevel({ logLevel: 'verbose' }) — см. Логирование) обёрнутая ошибка выводится в консоль, что обычно сразу указывает на реальную причину. Свойство detail у AdaptyError заполняется вне зависимости от уровня логирования — подробные логи просто выводят его в консоль.
try {
const result = await adapty.makePurchase({ product });
// Обработка результата покупки
if (result.type === 'success') {
console.log('Покупка успешна:', result.profile);
} else if (result.type === 'user_cancelled') {
console.log('Пользователь отменил покупку');
} else if (result.type === 'pending') {
console.log('Покупка ожидает подтверждения');
}
} catch (error) {
if (error instanceof AdaptyError) {
console.error('Ошибка Adapty:', error.adaptyCode, error.localizedDescription);
// Обработка конкретных кодов ошибок
switch (error.adaptyCode) {
case ErrorCodeName.cantMakePayments:
console.log('Встроенные покупки недоступны на этом устройстве');
break;
case ErrorCodeName.notActivated:
console.log('SDK Adapty не активирован');
break;
case ErrorCodeName.productPurchaseFailed:
console.log('Покупка не удалась:', error.detail);
break;
default:
console.log('Произошла другая ошибка:', error.detail);
}
} else {
console.error('Ошибка не связана с Adapty:', error);
}
}
Свойства ошибки
Класс AdaptyError предоставляет следующие свойства:
| Свойство | Тип | Описание |
|---|---|---|
adaptyCode | number | Числовой код ошибки (например, 1003 для cantMakePayments) |
localizedDescription | string | Понятное пользователю сообщение об ошибке |
detail | string | undefined | Дополнительная информация об ошибке (необязательно) |
message | string | Полное сообщение об ошибке с кодом и описанием |
Коды ошибок
SDK экспортирует константы и утилиты для работы с кодами ошибок:
Константа ErrorCodeName
Сопоставляет строковые идентификаторы с числовыми кодами:
ErrorCodeName.cantMakePayments // 1003
ErrorCodeName.notActivated // 2002
ErrorCodeName.networkFailed // 2005
Константа ErrorCode
Сопоставляет числовые коды со строковыми идентификаторами:
ErrorCode[1003] // 'cantMakePayments'
ErrorCode[2002] // 'notActivated'
ErrorCode[2005] // 'networkFailed'
Вспомогательные функции
// Get numeric code from string name:
getErrorCode('cantMakePayments') // 1003
// Get string name from numeric code:
getErrorPrompt(1003) // 'cantMakePayments'
Сравнение кодов ошибок
Важно: error.adaptyCode — это число, поэтому сравнивайте его напрямую с числовыми кодами:
// Option 1: Use ErrorCodeName constant (recommended) ✅
if (error.adaptyCode === ErrorCodeName.cantMakePayments) {
console.log('Cannot make payments');
}
// Option 2: Compare with numeric literal ✅
if (error.adaptyCode === 1003) {
console.log('Cannot make payments');
}
// NOT like this ❌ - compares number to string and will never match
if (error.adaptyCode === ErrorCode[1003]) {
}
Глобальный обработчик ошибок
Вы можете настроить глобальный обработчик ошибок для перехвата всех ошибок Adapty:
// Set up global error handler
AdaptyError.onError = (error: AdaptyError) => {
console.error('Global Adapty error:', {
code: error.adaptyCode,
message: error.localizedDescription,
detail: error.detail
});
// Handle specific error types globally
if (error.adaptyCode === ErrorCodeName.notActivated) {
// SDK not activated - maybe retry activation
console.log('SDK not activated, attempting to reactivate...');
}
};
Типовые паттерны обработки ошибок
Обработка ошибок при покупке
async function handlePurchase(product: AdaptyPaywallProduct) {
try {
const result = await adapty.makePurchase({ product });
if (result.type === 'success') {
console.log('Покупка выполнена успешно:', result.profile);
} else if (result.type === 'user_cancelled') {
console.log('Пользователь отменил покупку');
} else if (result.type === 'pending') {
console.log('Покупка ожидает подтверждения');
}
} catch (error) {
if (error instanceof AdaptyError) {
switch (error.adaptyCode) {
case ErrorCodeName.cantMakePayments:
console.log('Встроенные покупки не разрешены');
break;
case ErrorCodeName.productPurchaseFailed:
console.log('Покупка не выполнена:', error.detail);
break;
default:
console.error('Ошибка покупки:', error.localizedDescription);
}
}
}
}
Обработка сетевых ошибок
async function fetchPaywall(placementId: string) {
try {
const paywall = await adapty.getPaywall({ placementId });
return paywall;
} catch (error) {
if (error instanceof AdaptyError) {
switch (error.adaptyCode) {
case ErrorCodeName.networkFailed:
console.log('Network error, retrying...');
// Implement retry logic
break;
case ErrorCodeName.serverError:
console.log('Server error:', error.detail);
break;
case ErrorCodeName.notActivated:
console.log('SDK not activated');
break;
default:
console.error('Paywall fetch error:', error.localizedDescription);
}
}
throw error;
}
}
Системные коды StoreKit
| Ошибка | Код | Описание |
|---|---|---|
| unknown | 0 | Неизвестная или непредвиденная ошибка. |
| clientInvalid | 1 | Клиенту не разрешено выполнять запрошенное действие. |
| paymentCancelled | 2 | Пользователь отменил запрос на оплату. Действий не требуется, но с точки зрения бизнес-логики можно предложить пользователю скидку или напомнить о покупке позже. |
| paymentInvalid | 3 | Один из параметров платежа не был распознан стором. |
| paymentNotAllowed | 4 | Пользователю не разрешено авторизовывать платежи. Возможные причины: - Платежи не поддерживаются в стране пользователя. - Пользователь является несовершеннолетним. |
| storeProductNotAvailable | 5 | Запрошенный продукт отсутствует в App Store. Убедитесь, что продукт доступен для нужной страны. |
| cloudServicePermissionDenied | 6 | Пользователь не разрешил доступ к информации облачного сервиса. |
| cloudServiceNetworkConnectionFailed | 7 | Устройству не удалось подключиться к сети. |
| cloudServiceRevoked | 8 | Пользователь отозвал разрешение на использование облачного сервиса. |
| privacyAcknowledgementRequired | 9 | Пользователь ещё не ознакомился с политикой конфиденциальности стора. |
| unauthorizedRequestData | 10 | Запрос сформирован некорректно. |
| invalidOfferIdentifier | 11 | Идентификатор офера недействителен. Возможные причины: - Офер с таким идентификатором не настроен в App Store. - Офер был отозван. - Идентификатор офера указан с опечаткой. |
| invalidSignature | 12 | Подпись в платёжной скидке недействительна. Убедитесь, что заполнено поле In-app purchase Key ID и загружен файл In-App Purchase Private Key. Подробнее — в разделе Настройка интеграции с App Store. |
| missingOfferParams | 13 | Проблема с интеграцией Adapty или с оферами. Подробнее — в разделах Настройка интеграции с App Store и Оферы. |
| invalidOfferPrice | 14 | Указанная в сторе цена больше не действительна. Оферы всегда должны предоставлять скидку относительно обычной цены. |
Пользовательские коды Android
| Ошибка | Код | Описание |
|---|---|---|
| adaptyNotInitialized | 20 | Необходимо правильно настроить SDK Adapty с помощью метода Adapty.activate. Узнайте, как это сделать для React Native. |
| productNotFound | 22 | Запрошенный для покупки продукт недоступен в сторе. |
| invalidJson | 23 | JSON пейвола недействителен. Исправьте его в дашборде Adapty. Подробнее — в разделе Настройка пейвола с помощью Remote Config. |
| currentSubscriptionToUpdateNotFoundInHistory | 24 | Исходная подписка, которую необходимо обновить, не найдена. |
| pendingPurchase | 25 | Покупка находится в статусе ожидания, а не завершена. Подробнее — на странице Обработка отложенных транзакций в документации Android Developer. |
| billingServiceTimeout | 97 | Запрос превысил максимальное время ожидания до получения ответа от Google Play. Причиной может быть, например, задержка при выполнении действия, запрошенного вызовом Play Billing Library. |
| featureNotSupported | 98 | Запрошенная функция не поддерживается Play Store на текущем устройстве. |
| billingServiceDisconnected | 99 | Критическая ошибка: соединение клиентского приложения с сервисом Google Play Store через BillingClient разорвано. |
| billingServiceUnavailable | 102 | Временная ошибка: сервис Google Play Billing сейчас недоступен. В большинстве случаев это означает проблему с сетевым подключением между клиентским устройством и сервисами Google Play Billing. |
| billingUnavailable | 103 | Ошибка выставления счёта пользователю в процессе покупки. Примеры ситуаций, в которых это может произойти: 1. Приложение Play Store на устройстве пользователя устарело. 2. Пользователь находится в неподдерживаемой стране. 3. Пользователь является корпоративным, и администратор отключил для него возможность совершать покупки. 4. Google Play не может списать средства с платёжного метода пользователя. Например, срок действия кредитной карты истёк. 5. Пользователь не авторизован в приложении Play Store. |
| developerError | 105 | Критическая ошибка: некорректное использование API. |
| billingError | 106 | Критическая ошибка: внутренняя проблема самого Google Play. |
| itemAlreadyOwned | 107 | Расходуемая покупка уже была приобретена. |
| itemNotOwned | 108 | Запрошенное действие с элементом завершилось неудачей, так как он не принадлежит пользователю. |
Пользовательские коды StoreKit
| Ошибка | Код | Описание |
|---|---|---|
| noProductIDsFound | 1000 | Ни один из продуктов пейвола не доступен в сторе. Если вы столкнулись с этой ошибкой, выполните следующие шаги для её устранения: 1. Убедитесь, что все продукты добавлены в дашборд Adapty. 2. Проверьте, что Bundle ID приложения совпадает с указанным в Apple Connect. 3. Убедитесь, что идентификаторы продуктов из стора совпадают с теми, что добавлены в дашборд. Обратите внимание: идентификаторы не должны содержать Bundle ID, если только он уже не включён в идентификатор в сторе. 4. Убедитесь, что статус оплаты приложения активен в налоговых настройках Apple. Проверьте актуальность налоговой информации и действительность сертификатов. 5. Проверьте, привязан ли к приложению банковский счёт — это необходимо для монетизации. 6. Проверьте доступность продуктов во всех регионах. Убедитесь, что продукты находятся в статусе “Ready to Submit”. |
| productRequestFailed | 1002 | Не удаётся получить доступные продукты в данный момент. Возможная причина: - Кэш ещё не создан, и одновременно отсутствует подключение к интернету. |
| cantMakePayments | 1003 | Встроенные покупки не разрешены на этом устройстве. |
| noPurchasesToRestore | 1004 | Google Play не нашёл покупку для восстановления. |
| cantReadReceipt | 1005 | На устройстве нет действительного чека. Это может быть проблемой при тестировании в песочнице. Действий не требуется, но с точки зрения бизнес-логики можно предложить пользователю скидку или напомнить о покупке позже. |
| productPurchaseFailed | 1006 | Не удалось выполнить покупку продукта. Эта ошибка оборачивает базовую ошибку StoreKit — прочитайте вложенную ошибку (или включите подробные логи для просмотра в консоли), чтобы узнать реальную причину. Вложенная ошибка, как правило, соответствует одному из кодов StoreKit 0–14 из таблицы выше — чаще всего paymentCancelled, paymentInvalid, paymentNotAllowed или invalidOfferPrice. Если определить конкретную причину не удаётся, попробуйте создать новый профиль песочницы; если проблема сохраняется, обратитесь в поддержку Apple. |
| refreshReceiptFailed | 1010 | Чек не был получен. Применимо только к StoreKit 1. |
| receiveRestoredTransactionsFailed | 1011 | Не удалось восстановить покупки. |
Пользовательские сетевые коды
| Ошибка | Код | Описание |
|---|---|---|
| notActivated | 2002 | Необходимо правильно настроить SDK Adapty с помощью метода Adapty.activate. Узнайте, как это сделать для React Native. |
| badRequest | 2003 | Некорректный запрос. |
| serverError | 2004 | Ошибка сервера. |
| networkFailed | 2005 | Сетевой запрос не выполнен. |
| decodingFailed | 2006 | Ошибка декодирования ответа. |
| encodingFailed | 2009 | Ошибка кодирования запроса. |
| analyticsDisabled | 3000 | Невозможно обработать аналитические события, так как вы отключили их сбор. Подробнее — в разделе Интеграция аналитики. |
| wrongParam | 3001 | Один или несколько параметров некорректны: пустые там, где это недопустимо, или имеют неверный тип. |
| activateOnceError | 3005 | Метод .activate нельзя вызывать более одного раза. |
| profileWasChanged | 3006 | Профиль пользователя был изменён во время операции. |
| fetchTimeoutError | 3101 | Пейвол не удалось загрузить в установленный срок. Чтобы избежать этой ситуации, настройте локальные резервные пейволы. |
| operationInterrupted | 9000 | Операция была прервана системой. |