Устаревшие спецификации серверного API
Базовый URL: https://api.adapty.io/api/v1/sdk
Авторизация
Вы просматриваете гайд по устаревшему серверному API. Для актуальной версии обратитесь к Server-side API V2 и Руководству по миграции на Server-side API V2.
Каждый запрос к API должен быть подписан секретным ключом.
При вызове API:
- Необходимо добавлять заголовок Authorization со значением “Api-Key {secret_token}” (без кавычек) к каждому запросу, например
Api-Key secret_live_BEHrYLTr.ce5zuDEWz06lFRNiaJC8mrLtL8fUwswD - Используйте JSON-payload в теле запроса для POST и PATCH запросов
- Все запросы должны содержать заголовок Content-Type: application/json
Работа с customer user ID
Вы просматриваете гайд для устаревшего серверного API. Актуальная версия доступна в разделе Server-side API V2 и Руководство по миграции на Server-side API V2.
Большинство запросов к серверному API поддерживают передачу customer_user_id в качестве параметра URL. Это позволяет легко запрашивать и обновлять данные в Adapty, не сохраняя profile_id Adapty. В большинстве случаев customer_user_id следует передавать как есть, без каких-либо изменений.
Однако если ваш customer_user_id содержит зарезервированные символы URI, например /, ?, +, его нужно передавать в закодированном виде. Используйте кодировку Base64URL (не обычный Base64). Так все специальные символы будут закодированы, а Adapty декодирует их при получении. Чтобы сообщить Adapty, что customer_user_id закодирован, передайте GET-параметр is_user_id_base64url_encoded=1. Обратите внимание: если передать параметр is_user_id_base64url_encoded=1 без фактического кодирования, вернётся ошибка валидации 400.
Кодировать customer_user_id нужно только в том случае, если вы передаёте его как часть URL-пути. Если customer_user_id передаётся внутри JSON-тела запроса (например, при создании профиля), кодировать его не нужно.
## Don't encode
customer_user_id = '123' # GET: /profiles/123/
customer_user_id = 'abc' # GET: /profiles/abc/
customer_user_id = '3c410419-9959-447a-84b5-be7cb6a308d9' # GET: /profiles/3c410419-9959-447a-84b5-be7cb6a308d9/
## Base64URL encode
customer_user_id = '123+456' # GET: /profiles/MTIzKzQ1Ng==/?is_user_id_base64url_encoded=1
customer_user_id = 'abc/def' # GET: /profiles/YWJjL2RlZg==/?is_user_id_base64url_encoded=1
customer_user_id = '012?012' # GET: /profiles/MDEyPzAxMg==/?is_user_id_base64url_encoded=1
Запросы
Продление/предоставление подписки пользователю
Вы просматриваете гайд для устаревшего серверного API. Актуальная версия доступна в следующих запросах:
- Set Transaction: Добавление деталей транзакции с предоставлением доступа.
- Grant Access Level: Добавление или продление доступа без транзакции.
- Revoke Access Level: Сокращение или отзыв доступа без транзакции.
POST: /profiles/{profile_id_or_customer_user_id}/paid-access-levels/{access_level}/grant/
Параметры пути:
| Param | Type | Required | Nullable | Description |
|---|---|---|---|---|
| profile_id_or_customer_user_id | str | ✅ | ❌ | Adapty profile ID или внутренний ID разработчика |
| access_level | str | ✅ | ❌ | ID (slug) платного уровня доступа. Найдите его в дашборде Adapty |
Параметры запроса:
| Param | Type | Required | Nullable | Description |
|---|---|---|---|---|
| expires_at | ISO 8601 date | ✅* см. ниже | ❌ | Дата окончания подписки |
| duration_days | int | ✅* см. ниже | ❌ | Дополнительные дни к текущей подписке** |
| is_lifetime | bool | ✅* см. ниже | ❌ | Если установлено значение true, пользователь получит пожизненный доступ навсегда |
| starts_at | ISO 8601 date | ❌ | ❌ | Если время начала действия относится к будущему, его можно передать. Если указаны время начала и период, период будет отсчитываться от указанного времени |
| vendor_product_id | str | ❌ | ❌ | При публикации транзакции укажите ID продукта, который инициирует продление подписки. Если вы предоставляете уровень доступа без транзакции, пропустите этот параметр — по умолчанию будет использован adapty_server_side_product. |
| base_plan_id | str | ❌ | ❌ | ID базового плана в Google Play Store или ID цены в Stripe. |
| vendor_original_transaction_id | str | ❌ | ❌ | ID исходной транзакции в цепочке продлений подписки в среде вендора. |
| vendor_transaction_id | str | ❌ | ❌ | ID транзакции в среде вендора. Если значение совпадает с vendor_original_transaction_id или если vendor_original_transaction_id отсутствует, Adapty считает это первой покупкой подписки. Если значение отличается от vendor_original_transaction_id, Adapty считает покупку продлением подписки. |
| store | str | ❌ | ❌ | Стор, в котором пользователь приобрёл продукт, например app_store и play_store; может быть кастомным. По умолчанию — adapty |
| introductory_offer_type | str | ❌ | ❌ | Тип introductory offer. Доступные значения: free_trial, pay_as_you_go и pay_up_front. |
| price | float | ❌ | ❌ | Цена подписки/покупки для сохранения в транзакции. Первая покупка подписки с нулевой ценой считается бесплатным пробным периодом, а продление с нулевой ценой — бесплатным продлением подписки. Если вы указываете цену, укажите также |
| price_locale | str | ❌ | ❌ | Валюта транзакции в трёхбуквенном формате. По умолчанию используется USD. |
| proceeds | float | ❌ | ❌ | Выручка (цена за вычетом комиссии стора) подписки/покупки для сохранения в транзакции. |
| is_sandbox | bool | ❌ | ❌ | Булево значение, указывающее, была ли покупка совершена в песочнице или в продакшн-среде. |
Платный уровень доступа
Есть три способа предоставить пользователям подписку. Поэтому необходимо задать хотя бы один из параметров: is_lifetime, expires_at или duration_days. Если задано несколько параметров, приоритет имеет is_lifetime=true, затем expires_at, и наконец duration_days. Поскольку всю обработку платежей выполняет Apple/Google, Adapty не может управлять этим процессом или влиять на него. Поэтому при использовании duration_days для активной подписки помните: пользователь всё равно будет списан в нужный день. Например, у пользователя есть ежемесячная подписка, и следующее списание запланировано на 5 апреля. Вы добавляете 7 дней, но пользователь всё равно будет списан 5 апреля! Лучше использовать duration_days для пользователей, которые никогда не подписывались, или для отписавшихся. В этом случае отправной точкой будет день предоставления доступа.
Транзакция
Если указаны все параметры vendor_product_id, vendor_transaction_id и store, Adapty создаёт и сохраняет запись о транзакции, которая учитывается в графиках (Revenue, MRR, Subscriptions) — при условии, что транзакция с такими параметрами ещё не существует (например, созданная в результате покупки на iOS). Если указан параметр price, он привязывается к этой транзакции.
На данный момент такой тип транзакции не генерирует события профиля, не влияет на статус подписки пользователя и не отображается в Event Feed. Также учтите, что эти транзакции влияют на биллинг, поскольку учитываются в MTR.
Пример запроса:
{
"starts_at": "2020-01-15T15:10:36.517975+0000",
"expires_at": "2020-02-15T15:10:36.517975+0000",
"vendor_product_id": "basic_subscription_1_month",
"vendor_transaction_id": "1000000630116569",
"store": "app_store",
"introductory_offer_type": null
}
Пример ответа:
{
"data": {
"app_id": "ff90dd2e-e7f2-454b-9d86-071036a284fe",
"profile_id": "77112400-89f1-4465-b9c9-5437e58c6688",
"customer_user_id": "[email protected]",
"paid_access_levels": {
"premium": {
"id": "premium",
"is_active": true,
"is_lifetime": false,
"expires_at": "2023-03-29T15:30:34.000000+0000",
"starts_at": null,
"will_renew": false,
"vendor_product_id": "adapty_server_side_product",
"base_plan_id": "premium_autorenewing",
"vendor_transaction_id": "1000000630116569",
"vendor_original_transaction_id": "1000000625263604",
"store": "adapty",
"activated_at": "2020-03-26T16:24:19.497674+0000",
"renewed_at": "2020-03-26T16:24:19.497674+0000",
"unsubscribed_at": null,
"billing_issue_detected_at": null,
"is_in_grace_period": false,
"active_introductory_offer_type": "free_trial",
"active_promotional_offer_type": null,
"active_promotional_offer_id": null,
"cancellation_reason": null
}
},
"subscriptions": {
"com.adapty.premium.monthly": {
"is_active": false,
"is_lifetime": false,
"expires_at": "2020-02-21T16:30:34.000000+0000",
"starts_at": null,
"will_renew": false,
"vendor_product_id": "com.adapty.premium.monthly",
"base_plan_id": "monthly_autorenewing",
"vendor_transaction_id": "1000000630116569",
"vendor_original_transaction_id": "1000000625263604",
"store": "app_store",
"activated_at": "2020-02-10T19:14:02.000000+0000",
"renewed_at": "2020-02-21T16:25:34.000000+0000",
"unsubscribed_at": "2020-02-21T16:30:34.000000+0000",
"billing_issue_detected_at": "2020-02-21T16:30:34.000000+0000",
"is_in_grace_period": false,
"active_introductory_offer_type": null,
"active_promotional_offer_type": null,
"active_promotional_offer_id": null,
"cancellation_reason": "voluntarily_cancelled",
"is_sandbox": true
},
"com.adapty.premium.weekly": {
"is_active": false,
"is_lifetime": false,
"expires_at": "2020-02-10T19:32:00.000000+0000",
"starts_at": null,
"will_renew": true,
"vendor_product_id": "com.adapty.premium.weekly",
"base_plan_id": "weekly_autorenewing",
"vendor_transaction_id": "1000000625265713",
"vendor_original_transaction_id": "1000000625263604",
"store": "app_store",
"activated_at": "2020-02-10T19:14:02.000000+0000",
"renewed_at": "2020-02-10T19:29:00.000000+0000",
"unsubscribed_at": null,
"billing_issue_detected_at": null,
"is_in_grace_period": false,
"active_introductory_offer_type": null,
"active_promotional_offer_type": null,
"active_promotional_offer_id": null,
"cancellation_reason": null,
"is_sandbox": true
},
"basic_subscription_unlimited": {
"is_active": true,
"is_lifetime": false,
"expires_at": "2021-02-27T11:00:30.000000+0000",
"starts_at": null,
"will_renew": false,
"vendor_product_id": "basic_subscription_unlimited",
"base_plan_id": "basic_prepaid",
"vendor_transaction_id": "1000000632277988",
"vendor_original_transaction_id": "1000000632277988",
"store": "app_store",
"activated_at": "2020-02-27T11:00:30.000000+0000",
"renewed_at": null,
"unsubscribed_at": null,
"billing_issue_detected_at": null,
"is_in_grace_period": false,
"active_introductory_offer_type": null,
"active_promotional_offer_type": null,
"active_promotional_offer_id": null,
"cancellation_reason": null,
"is_sandbox": true
}
},
"non_subscriptions": null
}
}
Подробнее об ответах читайте в разделе Объекты API.
Отзыв подписки у пользователя
Вы просматриваете гайд для устаревшего серверного API. Для последней версии обратитесь к запросу Revoke Access Level, который позволяет как сократить, так и отозвать уровень доступа без транзакции.
POST: /profiles/{profile_id_or_customer_user_id}/paid-access-levels/{access_level}/revoke/
Параметры пути:
| Параметр | Тип | Обязательный | Nullable | Описание |
|---|---|---|---|---|
| profile_id_or_customer_user_id | str | ✅ | ❌ | ID профиля Adapty или внутренний ID разработчика |
| access_level | str | ✅ | ❌ | ID (slug) платного уровня доступа. Найдите его в дашборде Adapty |
Параметры запроса:
| Param | Type | Required | Nullable | Description |
|---|---|---|---|---|
| is_refund | bool | ✅ | ❌ | Была ли подписка отозвана в связи с возвратом средств |
| Отзывает подписку пользователя, устанавливая unsubscribed_at в текущее время, а expires_at — в максимальное из текущего starts_at и текущего времени (чтобы expires_at не оказалось меньше starts_at). Если с этим платным уровнем доступа связана транзакция, срок её истечения также обновляется до нового значения expires_at. Если is_refund равно true, транзакция помечается как возврат, а выручка обнуляется. |
Валидация покупки в Stripe, предоставление уровня доступа пользователю и импорт истории транзакций из Stripe
POST: /api/v1/sdk/purchase/stripe/token/validate/
Этот запрос должен использовать другой Content-Type: Content-Type: application/vnd.api+json'
Параметры запроса:
| Param | Type | Required | Nullable | Description |
|---|---|---|---|---|
| customer_user_id | str | ✅ | ❌ | Внутренний ID пользователя в системе разработчика |
| stripe_token | str | ✅ | ❌ | Токен объекта Stripe, представляющего уникальную покупку. Может быть токеном Stripe Subscription (sub_XXX) или Payment Intent (pi_XXX). |
Пример запроса:
curl
--location 'https://api.adapty.io/api/v1/sdk/purchase/stripe/token/validate/' \
--header 'Content-Type: application/vnd.api+json' \
--header 'Authorization: Api-Key <PUBLIC_OR_PRIVATE_KEY' \
--data-raw '{
"data": {
"type": "stripe_receipt_validation_result",
"attributes": {
"customer_user_id": "<CUSTOMER_USER_ID>",
"stripe_token": "sub_1OM8brJTlbIG45BdDRFOHWAU"
}
}
}'
Проверяет покупку по переданному токену Stripe, используя учётные данные Stripe из App Settings дашборда Adapty. Если покупка действительна, история транзакций импортируется из Stripe в профиль Adapty с указанным customer_user_id. Если профиля с таким customer_user_id не существовало — он будет создан.
В процессе генерируются события профиля, а импортированные транзакции учитываются в MTR.
Получение информации о пользователе
Вы просматриваете гайд по устаревшему серверному API. Для последней версии обратитесь к запросу Get profile.
GET: /profiles/{profile_id_or_customer_user_id}/
Параметры пути:
| Param | Type | Required | Nullable | Description |
|---|---|---|---|---|
| profile_id_or_customer_user_id | str | ✅ | ❌ | Adapty profile ID или внутренний ID разработчика |
Пример ответа такой же, как для Продления/предоставления подписки пользователю. Чтобы получить расширенный ответ, добавьте ключ “extended” с любым значением в параметры запроса (Query Params). Это работает только для GET-запросов.
| Свойство | Тип | Обязательное | Nullable | Описание |
|---|---|---|---|---|
| created_at | ISO 8601 date | ✅ | ❌ | Дата создания профиля, обычно совпадает с датой установки |
| str | ✅ | ✅ | Email пользователя | |
| phone_number | str | ✅ | ✅ | Номер телефона пользователя |
| att_status | str | ✅ | ✅ | |
| first_name | str | ✅ | ✅ | Имя пользователя |
| last_name | str | ✅ | ✅ | Фамилия пользователя |
| username | str | ✅ | ✅ | Имя пользователя (никнейм) |
| gender | str | ✅ | ✅ | Пол пользователя |
| birthday | ISO 8601 date | ✅ | ✅ | Дата рождения пользователя |
| idfa | str | ✅ | ✅ | Идентификатор для рекламодателей (IDFA), назначаемый Apple устройству пользователя. |
| idfv | str | ✅ | ✅ | Идентификатор для вендоров (IDFV) — код, присваиваемый всем приложениям одного разработчика и общий для всех его приложений на устройстве. |
| advertising_id | str | ✅ | ✅ | Advertising ID — уникальный идентификатор операционной системы Android, который рекламодатели могут использовать для однозначной идентификации пользователя. |
| appsflyer_id | str | ✅ | ✅ | AppsFlyer ID — идентификатор, автоматически создаваемый AppsFlyer при каждой новой установке приложения. |
| amplitude_user_id | str | ✅ | ✅ | Свойство Amplitude User ID и свойство External User ID в OneSignal должны быть заданы, чтобы данные сообщений для этого устройства отслеживались. |
| amplitude_device_id | str | ✅ | ✅ | Amplitude Device ID, поступающий непосредственно с устройств пользователей. |
| mixpanel_user_id | str | ✅ | ✅ | User ID из Mixpanel. |
| appmetrica_profile_id | str | ✅ | ✅ | ID профиля пользователя из AppMetrica. |
| appmetrica_device_id | str | ✅ | ✅ | Device ID из AppMetrica. |
| facebook_anonymous_id | str | ✅ | ✅ | Facebook Anonymous ID. |
Создание пользователя
Вы просматриваете гайд по устаревшему серверному API. Актуальная версия — в запросе Create profile.
POST: /profiles/
Параметры запроса:
| Параметр | Тип | Обязательный | Nullable | Описание |
|---|---|---|---|---|
| customer_user_id | str | ✅ | ❌ |
Пример запроса:
{
"customer_user_id": "123456"
}
Ответ аналогичен GET-запросу (параметр extended здесь не работает).
Также можно задать атрибуты пользователя так же, как в методе PATCH.
Задать атрибут пользователя
Вы просматриваете гайд для устаревшей версии серверного API. Для актуальной версии обратитесь к запросу Обновить профиль.
PATCH: /profiles/{profile_id_or_customer_user_id}/
Параметры пути:
| Параметр | Тип | Обязательный | Nullable | Описание |
|---|---|---|---|---|
| profile_id_or_customer_user_id | str | ✅ | ❌ | Adapty profile ID или внутренний ID разработчика |
Параметры запроса:
| Param | Type | Required | Nullable | Description |
|---|---|---|---|---|
| ip_country | str | ❌ | ✅ | Код страны в двухбуквенном формате, например US. |
| str | ❌ | ✅ | ||
| phone_number | str | ❌ | ✅ | |
| first_name | str | ❌ | ✅ | |
| last_name | str | ❌ | ✅ | |
| gender | str | ❌ | ✅ | Пол пользователя |
| birthday | date | ❌ | ✅ | Дата в формате YYYY-MM-DD, например 1990-10-31. |
Чтобы задать пользовательские атрибуты, передайте их в словаре custom_attributes. Для профиля можно задать не более 10 пользовательских атрибутов. В качестве значений допустимы только строки и числа с плавающей точкой; булевы значения будут автоматически преобразованы в числа с плавающей точкой. | ||||
| Param | Type | Required | Nullable | Description |
| :------------------- | :--------- | :------- | :------- | :----------------------------------------------------------- |
| attribute_key | str | ✅ | ❌ | Допустимы только буквы, цифры, дефисы, точки и символы подчёркивания. Длина ключа атрибута не должна превышать 30 символов. |
| attribute_value | str|float | ✅ | ✅ | Длина значения атрибута не должна превышать 30 символов. Передайте пустое значение или null, чтобы удалить атрибут. |
{
"phone_number": "+18003330000",
"custom_attributes": {
"grade": 10,
"favorite_topic": "sports"
}
}
Ответ аналогичен GET-запросу (параметр extended здесь не работает).
Удаление данных пользователя
Вы просматриваете гайд по устаревшему серверному API. Для последней версии обратитесь к запросу Удалить профиль.
DELETE /profiles/{profile_id_or_customer_user_id}/delete
Параметры пути:
| Param | Type | Required | Nullable | Description |
|---|---|---|---|---|
| profile_id_or_customer_user_id | str | ✅ | ❌ | Adapty profile ID или внутренний ID разработчика |
| Вызов этого эндпоинта удаляет профиль пользователя и все связанные с ним данные, делая их недоступными для клиента. История профиля, связанная с удалённым профилем, будет отвязана, а интеграционные события, ранее отправленные в интеграции, будут удалены из ленты событий. |
Если другой профиль совершит покупку с того же устройства с тем же Apple ID (или при восстановлении подписки), история профиля будет переназначена новому профилю, а интеграционные события будут отправлены повторно. Обратите внимание, что этот эндпоинт не поддерживает массовое удаление, поэтому каждый запрос обрабатывается отдельно. При работе с большим количеством пользователей рекомендуется выполнять запросы параллельно.