Adjust 是领先的移动测量合作伙伴(MMP)平台之一,负责收集和展示营销活动数据,帮助企业追踪营销活动效果。
Adapty 提供完整的数据集,让您可以在一处追踪来自各应用商店的订阅事件。借助 Adapty,您可以轻松了解订阅用户的行为规律,掌握他们的偏好,并据此以精准有效的方式与他们沟通。因此,通过此集成,您可以在 Adjust 中追踪订阅事件,并精确分析每个营销活动所带来的收入。
Adapty 与 Adjust 的集成主要通过以下两种方式实现。
- Adapty 从 Adjust 接收归因数据
完成 Adjust 集成配置后,Adapty 即可开始接收来自 Adjust 的归因数据。您可以在用户画像页面轻松查看这些数据。
- Adapty 向 Adjust 发送订阅事件
Adapty 可以将集成中配置的所有订阅事件发送至 Adjust。这样,您便可以在 Adjust 看板中追踪这些事件。此集成有助于评估广告营销活动的有效性。
设置集成
将 Adapty 连接至 Adjust
-
打开 Adapty 看板,导航至 Integrations > Adjust。
-
将页面顶部的开关设置为开启状态。
-
填写相关字段,设置您的访问凭据。
- 如果您在 Adjust 平台上启用了 OAuth 授权,则在集成过程中必须为您的 iOS 和 Android 应用提供 OAuth Token。
- 接下来,提供您 iOS 和 Android 应用的 app token。打开您的 Adjust 看板,即可看到您的应用列表。
您的 iOS 和 Android 可能对应不同的 Adjust 应用,因此在 Adapty 中分别有两个独立的配置区域。如果您只有一个 Adjust 应用,只需填入相同的信息即可。
- 从列表中选择您的应用并复制 App Token,然后将其粘贴到 Adapty 看板对应的字段中。
Adjust 的工作方式与其他平台略有不同。您需要在 Adjust 看板中手动创建事件,获取事件 token,然后将其复制粘贴到 Adapty 中对应的事件字段里。
因此,第一步是为所有您希望 Adapty 发送的事件查找对应的事件 token。具体操作如下:
- 在 Adjust 看板中,打开您的应用并切换到 Events 标签。
- 复制事件 token 并粘贴到 Adapty 中。在凭据字段下方,有三组事件可供您从 Adapty 发送至 Adjust。查看 Adapty 提供的完整事件列表,请参阅此处。
Adapty 将通过服务器间集成的方式向 Adjust 发送订阅事件,使您能够在 Adjust 看板中查看所有订阅事件,并将其与您的获客营销活动关联起来。
请注意以下事项:
- Adjust 不支持超过 58 天的历史事件。因此,如果某个事件距今已超过 58 天,Adapty 仍会将其发送至 Adjust,但事件时间戳将被替换为当前时间。
- Adjust 不支持 IPv6。如果您在 App settings 中或在 SDK 激活时禁用了 IP 收集,则可能只会发送后端的 IPv6 地址,导致追踪失败——请保持 SDK 的 IP 收集功能开启,以确保使用 IPv4。
将您的应用连接至 Adjust
完成上述步骤后,请在您的应用中添加以下两个方法。它们将建立您的应用与 Adjust 之间的通信:
- 向 Adjust 发送订阅数据:将 Adjust 设备 ID 传入
setIntegrationIdentifier() SDK 方法
- 从 Adjust 接收归因数据:使用
updateAttribution() SDK 方法更新归因数据
对于 Adjust 5.0 及更高版本,请参考以下示例:
class AdjustModuleImplementation {
func updateAdjustAdid() {
Adjust.adid { adid in
guard let adid else { return }
Adapty.setIntegrationIdentifier(key: "adjust_device_id", value: adid)
}
}
func updateAdjustAttribution() {
Adjust.attribution { attribution in
guard let attribution = attribution?.dictionary() else {
return
}
Adapty.updateAttribution(attribution, source: "adjust")
}
}
Adjust.getAdid { adid ->
if (adid == null) return@getAdid
Adapty.setIntegrationIdentifier("adjust_device_id", adid) { error ->
if (error != null) {
// handle the error
}
}
}
Adjust.getAttribution { attribution ->
if (attribution == null) return@getAttribution
Adapty.updateAttribution(attribution, "adjust") { error ->
// handle the error
}
}
Adjust.getAdid(adid -> {
if (adid == null) return;
Adapty.setIntegrationIdentifier("adjust_device_id", adid, error -> {
if (error != null) {
// handle the error
}
});
});
Adjust.getAttribution(attribution -> {
if (attribution == null) return;
Adapty.updateAttribution(attribution, "adjust", error -> {
// handle the error
});
});
import { Adjust, AdjustConfig } from "react-native-adjust";
import { adapty } from "react-native-adapty";
var adjustConfig = new AdjustConfig(appToken, environment);
// Before submiting Adjust config...
adjustConfig.setAttributionCallbackListener(attribution => {
// Make sure Adapty SDK is activated at this point
// You may want to lock this thread awaiting of `activate`
adapty.updateAttribution(attribution, "adjust");
});
// ...
Adjust.create(adjustConfig);
Adjust.getAdid((adid) => {
if (adid)
adapty.setIntegrationIdentifier("adjust_device_id", adid);
});
import 'package:adjust_sdk/adjust.dart';
import 'package:adjust_sdk/adjust_config.dart';
try {
final adid = await Adjust.getAdid();
if (adid == null) {
// handle the error
}
await Adapty().setIntegrationIdentifier(
key: "adjust_device_id",
value: adid,
);
final attributionData = await Adjust.getAttribution();
var attribution = Map<String, String>();
if (attributionData.trackerToken != null) attribution['trackerToken'] = attributionData.trackerToken!;
if (attributionData.trackerName != null) attribution['trackerName'] = attributionData.trackerName!;
if (attributionData.network != null) attribution['network'] = attributionData.network!;
if (attributionData.adgroup != null) attribution['adgroup'] = attributionData.adgroup!;
if (attributionData.creative != null) attribution['creative'] = attributionData.creative!;
if (attributionData.clickLabel != null) attribution['clickLabel'] = attributionData.clickLabel!;
if (attributionData.costType != null) attribution['costType'] = attributionData.costType!;
if (attributionData.costAmount != null) attribution['costAmount'] = attributionData.costAmount!.toString();
if (attributionData.costCurrency != null) attribution['costCurrency'] = attributionData.costCurrency!;
if (attributionData.fbInstallReferrer != null) attribution['fbInstallReferrer'] = attributionData.fbInstallReferrer!;
await Adapty().updateAttribution(attribution, source: "adjust");
} on AdaptyError catch (adaptyError) {
// handle the error
} catch (e) {
// handle the error
}
// 1. To update ADID
Adjust.GetAdid((adid) => {
if (adid == null) {
// handle the error
return;
}
Adapty.SetIntegrationIdentifier("adjust_device_id", adid, (error) => {
if (error != null) {
// handle the error
return;
}
});
});
// 2. To update Attribution
// in your adjust configuration scope:
adjustConfig.AttributionChangedDelegate = AttributionChangedCallback;
public void AttributionChangedCallback(AdjustAttribution attributionData) {
var attribution = new Dictionary<string, string>();
if (attributionData.TrackerToken != null) attribution["trackerToken"] = attributionData.TrackerToken;
if (attributionData.TrackerName != null) attribution["trackerName"] = attributionData.TrackerName;
if (attributionData.Network != null) attribution["network"] = attributionData.Network;
if (attributionData.Adgroup != null) attribution["adgroup"] = attributionData.Adgroup;
if (attributionData.Creative != null) attribution["creative"] = attributionData.Creative;
if (attributionData.ClickLabel != null) attribution["clickLabel"] = attributionData.ClickLabel;
if (attributionData.CostType != null) attribution["costType"] = attributionData.CostType;
if (attributionData.CostAmount != null) attribution["costAmount"] = attributionData.CostAmount.ToString();
if (attributionData.CostCurrency != null) attribution["costCurrency"] = attributionData.CostCurrency;
if (attributionData.FbInstallReferrer != null) attribution["fbInstallReferrer"] = attributionData.FbInstallReferrer;
// you will probably need to install Newtonsoft.Json package, if not yet
var attributionJsonString = Newtonsoft.Json.JsonConvert.SerializeObject(attribution);
Adapty.UpdateAttribution(attributionJsonString, "adjust", (error) => {
if (error != null) {
// handle the error
}
});
}
事件结构
Adapty 会根据 Adjust 集成页面上 Events names 部分的配置,将所选事件发送至 Adjust。每个事件的结构如下:
{
"event_token": "EVENT_TOKEN_FROM_CONFIG",
"app_token": "APP_TOKEN_FROM_CONFIG",
"s2s": 1,
"environment": "production",
"created_at_unix": 1709294400,
"currency": "USD",
"revenue": 9.99,
"customer_user_id": "user_12345",
"external_device_id": "user_12345",
"ip_address": "192.168.100.1",
"user_agent": "Mozilla/5.0 (Linux; Android 14; SM-S901B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
"android_id": "875646c2-4a56-4211-8931-168532479006",
"gps_adid": "875646c2-4a56-4211-8931-168532479006",
"callback_params": "{\"integration_event_id\":\"550e8400-e29b-41d4-a716-446655440000\",\"customer_user_id\":\"user_12345\",\"vendor_product_id\":\"com.example.app.yearly.premium\",\"transaction_id\":\"GPA.3312-4512-1100-55923\",\"original_transaction_id\":\"GPA.3312-4512-1100-55923\",\"store\":\"play_store\",\"store_country\":\"US\",\"price_usd\":9.99,\"proceeds_usd\":8.49,\"price_local\":9.99,\"proceeds_local\":8.49,\"net_revenue_usd\":8.49,\"net_revenue_local\":8.49,\"tax_amount_usd\":0.0,\"tax_amount_local\":0.0,\"consecutive_payments\":3,\"rate_after_first_year\":false}",
"partner_params": "{\"integration_event_id\":\"550e8400-e29b-41d4-a716-446655440000\",\"customer_user_id\":\"user_12345\",\"vendor_product_id\":\"com.example.app.yearly.premium\",\"transaction_id\":\"GPA.3312-4512-1100-55923\",\"original_transaction_id\":\"GPA.3312-4512-1100-55923\",\"store\":\"play_store\",\"store_country\":\"US\",\"price_usd\":9.99,\"proceeds_usd\":8.49,\"price_local\":9.99,\"proceeds_local\":8.49,\"net_revenue_usd\":8.49,\"net_revenue_local\":8.49,\"tax_amount_usd\":0.0,\"tax_amount_local\":0.0,\"consecutive_payments\":3,\"rate_after_first_year\":false}"
}
其中各参数说明如下:
| 参数 | 类型 | 描述 |
|---|
app_token | String | 集成设置中的 Adjust App Token。 |
event_token | String | 映射到特定 Adapty 事件的 Adjust Event Token。 |
s2s | Integer | 服务器间事件标识。 |
environment | String | sandbox 或 production。 |
created_at_unix | Integer | 事件时间戳(秒)。 |
currency | String | 交易的货币代码(例如”USD”)。仅当收入超过 0.001 时才包含此字段,因为 Adjust 要求收入和货币必须同时提供。 |
revenue | Float | 交易收入金额。仅当值超过 0.001 时才包含此字段。注意:退款事件不含收入属性,因为 Adjust 不支持负收入值。 |
customer_user_id | String | 用户的 Customer User ID。 |
external_device_id | String | 与 customer_user_id 相同。 |
ip_address | String | 用户 IP 地址(仅限 IPv4)。 |
user_agent | String | 设备 User Agent 字符串。 |
adid | String | Adjust 设备 ID(如果已知)。 |
android_id | String | 仅限 Android。Google 广告 ID。 |
gps_adid | String | 仅限 Android。Google 广告 ID。 |
idfa | String | 仅限 iOS。广告主标识符(IDFA)。 |
idfv | String | 仅限 iOS。供应商标识符(IDFV)。 |
callback_params | String | 包含所有可用事件字段的 JSON 字符串。仅包含非空字段。 |
partner_params | String | 与 callback_params 相同。 |
故障排查
收入数据不一致
如果 Adapty 与 Adjust 之间存在收入数据差异,可能是因为并非所有用户都在使用包含 Adapty SDK 的应用版本。为确保数据一致性,您可以强制用户将应用更新至集成了 Adapty SDK 的版本。