Track revenue
This page covers every SDK platform and version. A part that applies only to some platforms starts with Platform: …, and a part that applies only to some SDK versions starts with SDK version: ….
The justtrack SDK can track advertisement and in-app purchase revenue for you. Advertising revenue can also be tracked automatically using third-party SDK integrations (see Integrate third-party SDKs). If you're using an SDK we currently don't have an integration with, you can track ad impressions manually.
Track in-app purchase and subscription revenue
Platform: AndroidTo enable tracking of in-app purchase and subscription revenue, pass justtrack SDK’s installInstanceId as the obfuscated account ID when launching the Google Play Billing flow:
val installInstanceId = sdk.installInstanceId.await()
billing.launchBillingFlow(
context, BillingFlowParams.newBuilder()
.setObfuscatedAccountId(installInstanceId)
.build()
)
Afterward, follow our guide to setup Google Play real-time developer notifications in your dashboard to receive purchase and subscription events.
Platform: iOSIf the user performs an in-app product or subscription purchase, you can forward the purchase via the justtrack SDK to our backend. How you do this depends on the StoreKit version you're using:
- StoreKit 1
- StoreKit 2
For iOS using StoreKit 1, in-app purchases are tracked automatically, by default. However, you can use one of the following methods to change this behavior:
set(automaticInAppPurchaseTracking: Bool) -> Selfset(automaticInAppPurchaseTracking: Bool)
For example, you can use it when you build the SDK instance:
let builder = JustTrackSdkBuilder(apiToken: "prod-...") /* create the builder */
let sdk = builder
// configure the SDK when creating an instance:
.set(automaticInAppPurchaseTracking: forwardPurchasesAutomatically)
.build()
Alternatively, you can use it to configure an existing SDK instance:
sdk.set(automaticInAppPurchaseTracking: forwardPurchasesAutomatically)
When enabled, all purchases will be reported automatically to the justtrack backend and, if you have correctly configured purchase validation, show up as revenue for your app.
Fetch the install instance ID and pass it as the appAccountToken before calling product.purchase():
let installId = try await sdk.getInstallInstanceId().async()
guard let token = UUID(uuidString: installId) else {
print("Failed to create UUID from install instance ID: \(installId)")
return
}
let purchaseResult = try await product.purchase(options: [
.appAccountToken(token)
])
If the user performs an in-app product or subscription purchase, you can forward the purchase via the justtrack SDK to our backend. How you do this depends on the platform you're using:
- Android
- iOS (StoreKit 1)
- iOS (StoreKit 2)
To enable tracking of in-app purchase and subscription revenue, pass justtrack SDK’s installInstanceId as the obfuscated account ID when launching the Google Play Billing flow:
const installInstanceId = await JustTrackSdk.getInstallInstanceId();
await RNIap.requestPurchase({
sku: productId,
obfuscatedAccountIdAndroid: installInstanceId,
});
Afterward, follow our guide to setup Google Play real-time developer notifications in your dashboard to receive purchase and subscription events.
For iOS using StoreKit 1, in-app purchases are tracked automatically, by default. However, you can use one of the following methods to change this behavior:
withAutomaticInAppPurchaseTracking(enabled: boolean): JusttrackSdkBuilderasync function setAutomaticInAppPurchaseTracking(automaticInAppPurchaseTracking: boolean): Promise<void>
For example, you can use it when you build the SDK instance:
await new JustTrackSdk.JusttrackSdkBuilder(apiKey)
// configure the SDK when creating an instance:
.withAutomaticInAppPurchaseTracking(false)
// other options...
.initialize();
Alternatively, you can use it to configure an existing SDK instance:
JustTrackSdk.setAutomaticInAppPurchaseTracking(false);
When enabled, all purchases will be reported automatically to the justtrack backend and, if you have correctly configured purchase validation, show up as revenue for your app.
Forwarding in-app purchases from the SDK is only the first step toward seeing the data in justtrack. You also must set up a shared secret and server notifications. Read our documentation to learn how.
Pass the install instance ID when initiating the purchase so justtrack can match it to the user.
const installId = await JustTrackSdk.getInstallInstanceId();
const requestedPurchase = await reactIAP.requestPurchase({
sku: skus[0],
appAccountToken: installId,
});
Forwarding in-app purchases from the SDK is only the first step toward seeing the data in justtrack. You also must set up a shared secret and server notifications. Read our documentation to learn how.
If the user performs an in-app product or subscription purchase, you can forward the purchase via the justtrack SDK to our backend. How you do this depends on the platform you're using:
- Android
- iOS (StoreKit 1)
- iOS (StoreKit 2)
To enable tracking of in-app purchase and subscription revenue, pass justtrack SDK’s installInstanceId as the obfuscated account ID when launching the Google Play Billing flow:
#if UNITY_ANDROID
JustTrackSDK.GetInstallInstanceId(
installInstanceId =>
{
// Set installInstanceId as obfuscatedAccountId,
// so that the JustTrack backend can match the purchase with the user
var googlePlayConfig = builder.Configure<IGooglePlayConfiguration>();
googlePlayConfig.SetObfuscatedAccountId(installInstanceId);
UnityPurchasing.Initialize(this, builder);
},
error =>
{
// Failed to get installInstanceId — proceed with initialization anyway
UnityPurchasing.Initialize(this, builder);
});
#else
// If the platform is not Android, just initialize UnityPurchasing directly
UnityPurchasing.Initialize(this, builder);
#endif
Afterward, follow our guide to setup Google Play real-time developer notifications in your dashboard to receive purchase and subscription events.
For iOS using StoreKit 1, in-app purchases are tracked automatically, by default. However, you can use the following method to change this behavior:
JustTrackSDK.SetAutomaticInAppPurchaseTracking(forwardPurchasesAutomatically);
Alternatively, you can change this setting in the menu:

Forwarding in-app purchases from the SDK is only the first step toward seeing the data in justtrack. You also must set up a shared secret and server notifications. Read our documentation to learn how.
Pass the install instance ID when initiating the purchase so justtrack can match it to the user.
JustTrackSDK.GetInstallInstanceId(
installInstanceId =>
{
m_StoreController.AppleStoreExtendedService?.SetAppAccountToken(installInstanceId);
Debug.Log($"AppAccountToken set from JustTrack installInstanceId: {installInstanceId}");
},
error =>
{
Debug.LogWarning($"Failed to get JustTrack installInstanceId: {error}");
}
);
Forwarding in-app purchases from the SDK is only the first step toward seeing the data in justtrack. You also must set up a shared secret and server notifications. Read our documentation to learn how.
To track in-app purchase and subscription revenue, pass the justtrack SDK's installInstanceId to your purchase flow:
- Android
- iOS
Pass installInstanceId as the obfuscated account ID when launching the Google Play Billing flow:
final installInstanceId = await sdk.getInstallInstanceId();
// Using in_app_purchase package
final PurchaseParam purchaseParam = GooglePlayPurchaseParam(
productDetails: productDetails,
applicationUserName: installInstanceId,
);
await InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
Afterward, follow our guide to setup Google Play real-time developer notifications in your dashboard to receive purchase and subscription events.
Pass installInstanceId as the appAccountToken when making purchases with StoreKit 2:
final installInstanceId = await sdk.getInstallInstanceId();
// Convert installInstanceId to UUID format for appAccountToken
final uuid = Uuid.parse(installInstanceId);
// Using in_app_purchase package with StoreKit 2
final PurchaseParam purchaseParam = PurchaseParam(
productDetails: productDetails,
applicationUserName: installInstanceId,
);
await InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
The appAccountToken is included in Apple's server notifications, allowing the justtrack backend to match purchases with users.
Afterward, follow our guide to setup App Store server notifications in your dashboard to receive purchase and subscription events.
To track in-app purchase and subscription revenue, retrieve the install instance ID before initiating the purchase and pass it to your billing library:
- Android
- iOS
Connect to install_instance_id_received and pass the ID as the obfuscated account ID:
func _ready() -> void:
JustTrackSdk.install_instance_id_received.connect(_on_install_id)
JustTrackSdk.get_install_instance_id()
func _on_install_id(id: String) -> void:
# Pass `id` as the obfuscated account ID to your billing library
pass
Afterward, follow our guide to setup Google Play real-time developer notifications in your dashboard to receive purchase and subscription events.
Connect to install_instance_id_received and pass the ID as the appAccountToken (StoreKit 2):
func _ready() -> void:
JustTrackSdk.install_instance_id_received.connect(_on_install_id)
JustTrackSdk.get_install_instance_id()
func _on_install_id(id: String) -> void:
# Pass `id` as appAccountToken to your StoreKit 2 purchase call
pass
Afterward, follow our guide to setup App Store server notifications in your dashboard to receive purchase and subscription events.
- Android
- iOS
Retrieve the install instance ID with GetInstallInstanceId, then set it as the AccountId of the FPurchaseCheckoutRequest. The Google Play subsystem passes AccountId to BillingFlowParams.Builder.setObfuscatedAccountId() when it launches the Google Play Billing flow:
// Request the install instance ID before you start the checkout
FJustTrackInstallInstanceIdResult OnComplete;
OnComplete.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(AMyPlayerController, HandleInstallInstanceId));
JustTrack->GetInstallInstanceId(OnComplete);
// UFUNCTION() callback: add the ID to your existing checkout request
void AMyPlayerController::HandleInstallInstanceId(
bool bSuccess,
const FString& InstallInstanceId,
const FJustTrackError& Error)
{
FPurchaseCheckoutRequest Request;
Request.AddPurchaseOffer(TEXT(""), PendingProductId, 1);
if (bSuccess)
{
// Lets the justtrack backend match the purchase with the user
Request.AccountId = InstallInstanceId;
}
// If getting the ID failed, proceed with the purchase anyway
Purchase->Checkout(*UserId, Request, OnCheckoutComplete);
}
GetInstallInstanceId requires tracking to be running, so the callback fails while the SDK is stopped.
The engine's OnlineSubsystemGooglePlay plugin hashes AccountId with SHA-256 before it calls setObfuscatedAccountId(), so Google Play receives the hash rather than the raw install instance ID.
Afterward, follow our guide to setup Google Play real-time developer notifications in your dashboard to receive purchase and subscription events.
The engine's OnlineSubsystemIOS plugin uses StoreKit 1. For StoreKit 1, the justtrack SDK tracks in-app purchases automatically when you enable automatic in-app purchase tracking in your configuration, so you don't need to pass the install instance ID. Start purchases through IOnlinePurchase::Checkout() as usual:
FPurchaseCheckoutRequest Request;
Request.AddPurchaseOffer(TEXT(""), ProductId, 1);
Purchase->Checkout(*UserId, Request, OnCheckoutComplete);
Forwarding in-app purchases from the SDK is only the first step toward seeing the data in justtrack. You also must set up a shared secret and server notifications. Read our documentation to learn how.
Track ad impressions
Our SDK can forward ad impression data to the justtrack platform so you can track your app's ad revenue.
Automatic impression forwarding
We can automatically forward ad impressions for several partners. (See Integrate third-party SDKs) for documentation on automatic impression forwarding.
Manual impression forwarding
Platform: AndroidIf your ad network isn't available for automatic ad impression forwarding, you can manually forward impression data using .forwardAdImpression():
sdk.forwardAdImpression(
new AdImpression("unit","sdk_name")
.setNetwork("network")
.setPlacement("placement")
.setTestGroup("test_group")
.setSegmentName("segment_name")
.setInstanceName("instance_name")
.setBundleId("bundle_id")
.setRevenue(new Money(10.0, "USD")
);
This method accepts an AdImpression whose parameters are:
adUnit: The unit of the ad.adSdkName: The name of the SDK you used to fill the ad space, for exampleironsourceoradmob. When using ad mediation, this is the name of the mediation platform.
The SDK name must be lowercase. So, for example, you would use ironsource, not IronSource.
Additionally, you can use the following methods to set more dimensions:
| Method | Argument | Acceptable values |
|---|---|---|
.setNetwork() | The name of the ad network that provided the ad. When using ad mediation, this is the ad network that won the bid for the ad space. | String value or null. |
.setPlacement() | The placement of the ad. | String value or null. |
.setTestGroup() | The test group of the user, if you are A/B testing. | String value or null. |
.setSegmentName() | The name of the segment of the ad. | String value or null. |
.setInstanceName() | The name of the instance of the ad. | String value or null. |
.setBundleId() | The bundle ID for the app being advertised. | String value or null. |
.setRevenue() | The amount of revenue this ad generated. | Money object with a non-negative amount. The currency must be a three-letter uppercase ISO 4217 string. |
Upon success .forwardAdImpression() returns true. If a parameter is invalid, false is returned.
If your ad network isn't available for automatic ad impression forwarding, you can manually forward impression data using .forward():
let forwardingResult = sdk.forward(adImpression: AdImpression(unit: "unit", sdkName: "sdk_name")
.set(network: "network")
.set(placement: "placement")
.set(testGroup: "test_group")
.set(segmentName: "segment_name")
.set(instanceName: "instance_name")
.set(bundleId: "bundle_id")
.set(revenue: Money(value: 10.0, currency: "USD"))
)
switch forwardingResult {
case let .failure(error):
log("Error: \(error.localizedDescription)")
case .success:
log("Success")
}
}
This method accepts an AdImpression whose parameters are:
unit: The ad unit, e.g. banner, interstitial, rewarded, and so on.sdkName: The name of the SDK you used to fill the ad space, for exampleironsourceoradmob. When using ad mediation, this is the name of the mediation platform.
The SDK name must be lowercase. So, for example, you would use ironsource, not IronSource.
Additionally, you can use .set() with the following parameters to set more dimensions:
| Method | Argument | Acceptable values |
|---|---|---|
network | The name of the ad network that provided the ad. When using ad mediation, this is the ad network that won the bid for the ad space. | String value or null. |
placement | The placement of the ad. | String value or null. |
testGroup | The test group of the user, if you are A/B testing. | String value or null. |
segmentName | The name of the segment of the ad. | String value or null. |
instanceName | The name of the instance of the ad. | String value or null. |
bundleId | The bundle ID for the app being advertised. | String value or null. |
revenue | The amount of revenue this ad generated. | Money object with a non-negative amount. The currency must be a three-letter uppercase ISO 4217 string. |
Upon success .forward() returns .success, otherwise it returns .failure with an error.
If your ad network isn't available for automatic ad impression forwarding, you can manually forward impression data using .forwardAdImpression():
await JustTrackSdk.forwardAdImpression(
new JustTrackSdk.AdImpression(JustTrackSdk.AdUnit.Banner, 'ad_sdk_name')
.withAdNetwork('network')
.withPlacement('placement')
.withTestGroup('testgroup')
.withSegmentName('segment_name')
.withInstanceName('instance_name')
.withBundleId('bundle_id')
.withRevenue({ value: 10, currency: 'USD' }),
);
This method accepts an AdImpression whose parameters are:
adUnit: The unit of the ad.adSdkName: The name of the SDK you used to fill the ad space, for exampleironsourceoradmob. When using ad mediation, this is the name of the mediation platform.
The SDK name must be lowercase. So, for example, you would use ironsource, not IronSource.
Additionally, you can use the following methods to set more dimensions:
| Method | Argument | Acceptable values |
|---|---|---|
.withAdNetwork() | The name of the ad network that provided the ad. When using ad mediation, this is the ad network that won the bid for the ad space. | String value or null. |
.withPlacement() | The placement of the ad. | String value or null. |
.withTestGroup() | The test group of the user, if you are A/B testing. | String value or null. |
.withSegmentName() | The name of the segment of the ad. | String value or null. |
.withInstanceName() | The name of the instance of the ad. | String value or null. |
.withBundleId() | The bundle ID for the app being advertised. | String value or null. |
.withRevenue() | The amount of revenue this ad generated. | Money object with a non-negative amount. The currency must be a three-letter uppercase ISO 4217 string. |
Upon success .forwardAdImpression() returns true. If a parameter is invalid, false is returned.
If your ad network isn't available for automatic ad impression forwarding, you can manually forward impression data using .ForwardAdImpression():
JustTrackSDK.ForwardAdImpression(
new AdImpression("unit","sdk_name")
.SetNetwork("network")
.SetPlacement("placement")
.SetTestGroup("test_group")
.SetSegmentName("segment_name")
.SetInstanceName("instance_name")
.SetBundleId("bundle_id")
.SetRevenue(new Money(10.0,"USD")
);
This method accepts an AdImpression whose parameters are:
pUnit: The ad unit, e.g. banner, interstitial and so on.pSdkName: The name of the SDK which provided the event, for exampleironsourceoradmob.
The SDK name must be lowercase. So, for example, you would use ironsource, not ironSource.
Additionally, you can use the following methods to set more dimensions:
| Method | Argument | Acceptable values |
|---|---|---|
.SetNetwork() | The name of the ad network that provided the ad. | String value or null. |
.SetPlacement() | The placement of the ad. | String value or null. |
.SetTestGroup() | The test group of the user, if you are A/B testing. | String value or null. |
.SetSegmentName() | The name of the segment of the ad. | String value or null. |
.SetInstanceName() | The name of the instance of the ad. | String value or null. |
.SetBundleId() | The app's bundle ID. | String value or null. |
.SetRevenue() | The amount of revenue this ad generated. | Money object with a non-negative amount. The currency must be a three-letter uppercase ISO 4217 string. |
Upon success, .ForwardAdImpression() returns true. If a parameter is invalid, false is returned.
If your ad network isn't available for automatic ad impression forwarding, you can manually forward impression data using .forwardAdImpression():
await JusttrackSdk.forwardAdImpression(
AdImpression(
unit: AdUnit.banner,
adSdkName: 'admob',
adNetwork: 'ad_network',
placement: 'home_screen',
testGroup: 'test_group',
segmentName: 'segment_name',
instanceName: 'instance_name',
bundleId: 'com.example.app',
revenue: Money(0.01, 'USD'),
),
);
This method accepts an AdImpression whose parameters are:
unit: The ad unit, e.g. banner, interstitial, rewarded, and so on.adSdkName: The name of the SDK you used to fill the ad space, for exampleadmoborironsource. When using ad mediation, this is the name of the mediation platform.
The SDK name must be lowercase. So, for example, you would use ironsource, not IronSource.
If your ad network isn't available for automatic ad impression forwarding, you can manually forward
impression data using forward_ad_impression():
var impression := JustTrackSdk.AdImpression.new()
impression.ad_unit = JustTrackSdk.AdImpression.AD_UNIT_BANNER
impression.sdk_name = "admob"
impression.network = "ad_network"
impression.placement = "home_screen"
impression.test_group = "test_group"
impression.segment_name = "segment_name"
impression.instance_name = "instance_name"
impression.bundle_id = "com.example.app"
impression.revenue = {"value": 0.01, "currency": "USD"}
JustTrackSdk.ad_impression_completed.connect(func(req_id): print("Ad impression forwarded"))
JustTrackSdk.ad_impression_failed.connect(func(req_id, code, msg): push_error(msg))
JustTrackSdk.forward_ad_impression(impression)
The AdImpression properties are:
ad_unit: The ad unit type. Use one of theAD_UNIT_*constants:AD_UNIT_BANNER,AD_UNIT_INTERSTITIAL,AD_UNIT_REWARDED,AD_UNIT_REWARDED_INTERSTITIAL,AD_UNIT_NATIVE,AD_UNIT_APP_OPEN.sdk_name: The name of the SDK you used to fill the ad space, for exampleadmoborironsource. When using ad mediation, this is the name of the mediation platform.
The SDK name must be lowercase. So, for example, you would use ironsource, not IronSource.
Optional properties: network, placement, test_group, segment_name, instance_name, bundle_id, revenue (a Dictionary {"value": float, "currency": "USD"}).
If your ad network isn't available for automatic ad impression forwarding, you can manually forward
impression data using ForwardAdImpression:
FJustTrackAdImpression Impression;
Impression.AdUnit = EJustTrackAdUnit::Banner;
Impression.AdSdk = TEXT("admob");
Impression.AdNetwork = TEXT("ad_network");
Impression.AdPlacement = TEXT("home_screen");
Impression.AdTestGroup = TEXT("test_group");
Impression.AdSegment = TEXT("segment_name");
Impression.AdInstanceName = TEXT("instance_name");
Impression.AdBundleId = TEXT("com.example.app");
Impression.AdRevenue = 0.01;
Impression.AdCurrency = TEXT("USD");
FJustTrackVoidResult OnComplete;
OnComplete.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(AMyActor, HandleAdImpressionForwarded));
JustTrack->ForwardAdImpression(Impression, OnComplete);
This method accepts an FJustTrackAdImpression whose required fields are:
AdUnit: The ad unit type. Use one of theEJustTrackAdUnitvalues:Banner,Interstitial,Rewarded,RewardedInterstitial,Native, orAppOpen.AdSdk: The name of the SDK you used to fill the ad space, for exampleadmoborironsource. When using ad mediation, this is the name of the mediation platform. If it's empty, the callback fails with anINVALID_ARGUMENTSerror.
The SDK name must be lowercase. So, for example, you would use ironsource, not IronSource.
Additionally, you can set the following fields to add more dimensions:
| Field | Argument | Acceptable values |
|---|---|---|
AdNetwork | The name of the ad network that provided the ad. When using ad mediation, this is the ad network that won the bid for the ad space. | String value. Leave empty to omit it. |
AdPlacement | The placement of the ad. | String value. Leave empty to omit it. |
AdTestGroup | The test group of the user, if you are A/B testing. | String value. Leave empty to omit it. |
AdSegment | The name of the segment of the ad. | String value. Leave empty to omit it. |
AdInstanceName | The name of the instance of the ad. | String value. Leave empty to omit it. |
AdBundleId | The bundle ID for the app being advertised. | String value. Leave empty to omit it. |
AdRevenue | The amount of revenue this ad generated. | Non-negative double. Only forwarded when AdCurrency is set. |
AdCurrency | The currency of AdRevenue. | Three-letter uppercase ISO 4217 string. Leave empty to forward without revenue. |
The OnComplete callback reports whether the impression was forwarded. On failure, Error.Code and Error.Message describe what went wrong.
Use ForwardAdImpression for ad revenue. To record ad interactions that don't generate revenue, such as load, click, and close, use TrackAdEvent, which tracks the JtAd predefined event.