diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/ActivityHandler.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/ActivityHandler.java index 9b78a73fe..8556e8a9a 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/ActivityHandler.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/ActivityHandler.java @@ -805,11 +805,21 @@ public void run() { } @Override - public void verifyPurchase(final AdjustPurchase purchase, final OnPurchaseVerificationFinishedListener callback) { + public void verifyPlayStorePurchase(final AdjustPlayStorePurchase purchase, final OnPurchaseVerificationFinishedListener callback) { executor.submit(new Runnable() { @Override public void run() { - verifyPurchaseI(purchase, callback); + verifyPlayStorePurchaseI(purchase, callback); + } + }); + } + + @Override + public void verifyAndTrackPlayStorePurchase(AdjustEvent event, OnPurchaseVerificationFinishedListener callback) { + executor.submit(new Runnable() { + @Override + public void run() { + verifyAndTrackPlayStorePurchaseI(event, callback); } }); } @@ -2494,7 +2504,8 @@ private void trackPlayStoreSubscriptionI(final AdjustPlayStoreSubscription subsc packageHandler.sendFirstPackage(); } - private void verifyPurchaseI(final AdjustPurchase purchase, final OnPurchaseVerificationFinishedListener callback) { + private void verifyPlayStorePurchaseI(final AdjustPlayStorePurchase purchase, + final OnPurchaseVerificationFinishedListener callback) { if (callback == null) { logger.warn("Purchase verification aborted because verification callback is null"); return; @@ -2563,6 +2574,77 @@ private void verifyPurchaseI(final AdjustPurchase purchase, final OnPurchaseVeri purchaseVerificationHandler.sendPurchaseVerificationPackage(verificationPackage); } + private void verifyAndTrackPlayStorePurchaseI(final AdjustEvent event, + final OnPurchaseVerificationFinishedListener callback) { + if (callback == null) { + logger.warn("Purchase verification aborted because verification callback is null"); + return; + } + // from this moment on we know that we can ping client callback in case of error + if (adjustConfig.isDataResidency) { + logger.warn("Purchase verification not available for data residency users right now"); + AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult( + "not_verified", + 109, + "Purchase verification not available for data residency users right now"); + callback.onVerificationFinished(result); + return; + } + if (!checkActivityStateI(activityState)) { + AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult( + "not_verified", + 102, + "Purchase verification aborted because SDK is still not initialized"); + callback.onVerificationFinished(result); + logger.warn("Purchase verification aborted because SDK is still not initialized"); + return; + } + if (!isEnabledI()) { + AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult( + "not_verified", + 103, + "Purchase verification aborted because SDK is disabled"); + callback.onVerificationFinished(result); + logger.warn("Purchase verification aborted because SDK is disabled"); + return; + } + if (activityState.isGdprForgotten) { + AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult( + "not_verified", + 104, + "Purchase verification aborted because user is GDPR forgotten"); + callback.onVerificationFinished(result); + logger.warn("Purchase verification aborted because user is GDPR forgotten"); + return; + } + if (event == null) { + logger.warn("Purchase verification aborted because event instance is null"); + AdjustPurchaseVerificationResult verificationResult = + new AdjustPurchaseVerificationResult( + "not_verified", + 106, + "Purchase verification aborted because event instance is null"); + callback.onVerificationFinished(verificationResult); + return; + } + + long now = System.currentTimeMillis(); + PackageBuilder packageBuilder = new PackageBuilder(adjustConfig, deviceInfo, activityState, globalParameters, now); + ActivityPackage verificationPackage = packageBuilder.buildVerificationPackage(event, callback); + if (verificationPackage == null) { + logger.warn("Purchase verification aborted because verification package is null"); + AdjustPurchaseVerificationResult verificationResult = + new AdjustPurchaseVerificationResult( + "not_verified", + 107, + "Purchase verification aborted because verification package is null"); + callback.onVerificationFinished(verificationResult); + return; + } + purchaseVerificationHandler.sendPurchaseVerificationPackage(verificationPackage); + trackEventI(event); + } + private void setCoppaComplianceI(final boolean coppaEnabled) { if (activityState == null) { return; } if (!isEnabledI()) { return; } diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java index 3ba3cc994..bf454bbc6 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/Adjust.java @@ -431,13 +431,25 @@ public static void getSdkVersion(final OnSdkVersionReadListener onSdkVersionRead * @param purchase AdjustPurchase object to be tracked * @param callback Callback to obtain verification results */ - public static void verifyPurchase(final AdjustPurchase purchase, final OnPurchaseVerificationFinishedListener callback) { + public static void verifyPlayStorePurchase(final AdjustPlayStorePurchase purchase, + final OnPurchaseVerificationFinishedListener callback) { if (callback == null) { AdjustFactory.getLogger().error("Purchase verification aborted because verification callback is null"); return; } AdjustInstance adjustInstance = Adjust.getDefaultInstance(); - adjustInstance.verifyPurchase(purchase, callback); + adjustInstance.verifyPlayStorePurchase(purchase, callback); + } + + /** + * Verify in app purchase from Google Play and track Adjust event associated with it. + * + * @param event AdjustEvent object to be tracked + * @param callback Callback to obtain verification results + */ + public static void verifyAndTrackPlayStorePurchase(final AdjustEvent event, OnPurchaseVerificationFinishedListener callback) { + AdjustInstance adjustInstance = Adjust.getDefaultInstance(); + adjustInstance.verifyAndTrackPlayStorePurchase(event, callback); } /** diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustInstance.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustInstance.java index 989107aac..fb09c786e 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustInstance.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustInstance.java @@ -771,7 +771,8 @@ private boolean isInstanceEnabled() { * @param purchase AdjustPurchase object to be verified * @param callback Callback to be pinged with the verification results */ - public void verifyPurchase(final AdjustPurchase purchase, final OnPurchaseVerificationFinishedListener callback) { + public void verifyPlayStorePurchase(final AdjustPlayStorePurchase purchase, + final OnPurchaseVerificationFinishedListener callback) { if (!checkActivityHandler("verifyPurchase")) { AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult( "not_verified", @@ -780,7 +781,27 @@ public void verifyPurchase(final AdjustPurchase purchase, final OnPurchaseVerifi callback.onVerificationFinished(result); return; } - activityHandler.verifyPurchase(purchase, callback); + activityHandler.verifyPlayStorePurchase(purchase, callback); + } + + /** + * Verify in app purchase from Google Play and track Adjust event associated with it. + * + * @param event AdjustEvent to be tracked + * @param callback Callback to be pinged with the verification results + */ + public void verifyAndTrackPlayStorePurchase(AdjustEvent event, OnPurchaseVerificationFinishedListener callback) { + if (!checkActivityHandler("verifyAndTrack")) { + if (callback != null) { + AdjustPurchaseVerificationResult result = new AdjustPurchaseVerificationResult( + "not_verified", + 100, + "SDK needs to be initialized before making purchase verification request"); + callback.onVerificationFinished(result); + } + return; + } + activityHandler.verifyAndTrackPlayStorePurchase(event, callback); } /** diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustPurchase.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustPlayStorePurchase.java similarity index 71% rename from Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustPurchase.java rename to Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustPlayStorePurchase.java index 25e32ed1d..20361f446 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustPurchase.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/AdjustPlayStorePurchase.java @@ -1,10 +1,10 @@ package com.adjust.sdk; -public class AdjustPurchase { +public class AdjustPlayStorePurchase { private final String productId; private final String purchaseToken; - public AdjustPurchase(final String productId, final String purchaseToken) { + public AdjustPlayStorePurchase(final String productId, final String purchaseToken) { this.productId = productId; this.purchaseToken = purchaseToken; } diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/IActivityHandler.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/IActivityHandler.java index d5cc376b4..f54f58bd1 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/IActivityHandler.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/IActivityHandler.java @@ -74,7 +74,9 @@ public interface IActivityHandler { void trackPlayStoreSubscription(AdjustPlayStoreSubscription subscription); - void verifyPurchase(AdjustPurchase purchase, OnPurchaseVerificationFinishedListener callback); + void verifyPlayStorePurchase(AdjustPlayStorePurchase purchase, OnPurchaseVerificationFinishedListener callback); + + void verifyAndTrackPlayStorePurchase(AdjustEvent event, OnPurchaseVerificationFinishedListener callback); void setCoppaCompliance(boolean enabled); diff --git a/Adjust/sdk-core/src/main/java/com/adjust/sdk/PackageBuilder.java b/Adjust/sdk-core/src/main/java/com/adjust/sdk/PackageBuilder.java index 5074c23c2..71266884e 100644 --- a/Adjust/sdk-core/src/main/java/com/adjust/sdk/PackageBuilder.java +++ b/Adjust/sdk-core/src/main/java/com/adjust/sdk/PackageBuilder.java @@ -209,7 +209,7 @@ ActivityPackage buildSubscriptionPackage(AdjustPlayStoreSubscription subscriptio return subscriptionPackage; } - ActivityPackage buildVerificationPackage(AdjustPurchase purchase, OnPurchaseVerificationFinishedListener callback) { + ActivityPackage buildVerificationPackage(AdjustPlayStorePurchase purchase, OnPurchaseVerificationFinishedListener callback) { Map parameters = getVerificationParameters(purchase); ActivityPackage purchaseVerificationPackage = getDefaultActivityPackage(ActivityKind.PURCHASE_VERIFICATION); purchaseVerificationPackage.setPath("/verify"); @@ -220,6 +220,17 @@ ActivityPackage buildVerificationPackage(AdjustPurchase purchase, OnPurchaseVeri return purchaseVerificationPackage; } + ActivityPackage buildVerificationPackage(AdjustEvent event, OnPurchaseVerificationFinishedListener callback) { + Map parameters = getVerificationParameters(event); + ActivityPackage purchaseVerificationPackage = getDefaultActivityPackage(ActivityKind.PURCHASE_VERIFICATION); + purchaseVerificationPackage.setPath("/verify"); + purchaseVerificationPackage.setSuffix(""); + purchaseVerificationPackage.setPurchaseVerificationCallback(callback); + + purchaseVerificationPackage.setParameters(parameters); + return purchaseVerificationPackage; + } + private Map getSessionParameters() { Map parameters = new HashMap(); @@ -1050,7 +1061,7 @@ private Map getSubscriptionParameters(AdjustPlayStoreSubscriptio return parameters; } - private Map getVerificationParameters(AdjustPurchase purchase) { + private Map getVerificationParameters(AdjustPlayStorePurchase purchase) { Map parameters = new HashMap(); deviceInfo.reloadOtherDeviceInfoParams(adjustConfig, coppaEnabled, logger); @@ -1140,6 +1151,95 @@ private Map getVerificationParameters(AdjustPurchase purchase) { return parameters; } + private Map getVerificationParameters(AdjustEvent event) { + Map parameters = new HashMap(); + + deviceInfo.reloadOtherDeviceInfoParams(adjustConfig, coppaEnabled, logger); + + // Check if plugin is used and if yes, add read parameters. + if (deviceInfo.imeiParameters != null) { + parameters.putAll(deviceInfo.imeiParameters); + } + + // Check if oaid plugin is used and if yes, add the parameter + if (deviceInfo.oaidParameters != null) { + parameters.putAll(deviceInfo.oaidParameters); + } + + // Device identifiers. + deviceInfo.reloadPlayIds(adjustConfig, coppaEnabled, playStoreKidsAppEnabled); + PackageBuilder.addString(parameters, "android_uuid", activityStateCopy.uuid); + PackageBuilder.addString(parameters, "gps_adid", deviceInfo.playAdId); + PackageBuilder.addLong(parameters, "gps_adid_attempt", deviceInfo.playAdIdAttempt); + PackageBuilder.addString(parameters, "gps_adid_src", deviceInfo.playAdIdSource); + PackageBuilder.addBoolean(parameters, "tracking_enabled", deviceInfo.isTrackingEnabled); + PackageBuilder.addString(parameters, "fire_adid", deviceInfo.fireAdId); + PackageBuilder.addBoolean(parameters, "fire_tracking_enabled", deviceInfo.fireTrackingEnabled); + + if (!containsPlayIds(parameters) && !containsFireIds(parameters)) { + logger.warn("Google Advertising ID or Fire Advertising ID not detected, " + + "fallback to non Google Play and Fire identifiers will take place"); + deviceInfo.reloadNonPlayIds(adjustConfig, coppaEnabled, playStoreKidsAppEnabled); + PackageBuilder.addString(parameters, "android_id", deviceInfo.androidId); + } + + // Rest of the parameters. + PackageBuilder.addString(parameters, "api_level", deviceInfo.apiLevel); + PackageBuilder.addString(parameters, "app_token", adjustConfig.appToken); + PackageBuilder.addString(parameters, "app_version", deviceInfo.appVersion); + PackageBuilder.addBoolean(parameters, "attribution_deeplink", true); + PackageBuilder.addLong(parameters, "connectivity_type", deviceInfo.connectivityType); + PackageBuilder.addString(parameters, "country", deviceInfo.country); + PackageBuilder.addString(parameters, "cpu_type", deviceInfo.abi); + PackageBuilder.addDateInMilliseconds(parameters, "created_at", createdAt); + PackageBuilder.addString(parameters, "default_tracker", adjustConfig.defaultTracker); + PackageBuilder.addBoolean(parameters, "needs_cost", adjustConfig.needsCost); + PackageBuilder.addString(parameters, "device_manufacturer", deviceInfo.deviceManufacturer); + PackageBuilder.addString(parameters, "device_name", deviceInfo.deviceName); + PackageBuilder.addString(parameters, "device_type", deviceInfo.deviceType); + PackageBuilder.addLong(parameters, "ui_mode", deviceInfo.uiMode); + PackageBuilder.addString(parameters, "display_height", deviceInfo.displayHeight); + PackageBuilder.addString(parameters, "display_width", deviceInfo.displayWidth); + PackageBuilder.addString(parameters, "environment", adjustConfig.environment); + PackageBuilder.addString(parameters, "external_device_id", adjustConfig.externalDeviceId); + PackageBuilder.addString(parameters, "fb_id", deviceInfo.fbAttributionId); + PackageBuilder.addString(parameters, "hardware_name", deviceInfo.hardwareName); + PackageBuilder.addString(parameters, "installed_at", deviceInfo.appInstallTime); + PackageBuilder.addString(parameters, "language", deviceInfo.language); + PackageBuilder.addDuration(parameters, "last_interval", activityStateCopy.lastInterval); + PackageBuilder.addString(parameters, "mcc", deviceInfo.mcc); + PackageBuilder.addString(parameters, "mnc", deviceInfo.mnc); + PackageBuilder.addBoolean(parameters, "needs_response_details", true); + PackageBuilder.addString(parameters, "os_build", deviceInfo.buildName); + PackageBuilder.addString(parameters, "os_name", deviceInfo.osName); + PackageBuilder.addString(parameters, "os_version", deviceInfo.osVersion); + PackageBuilder.addString(parameters, "package_name", deviceInfo.packageName); + PackageBuilder.addString(parameters, "push_token", activityStateCopy.pushToken); + PackageBuilder.addString(parameters, "screen_density", deviceInfo.screenDensity); + PackageBuilder.addString(parameters, "screen_format", deviceInfo.screenFormat); + PackageBuilder.addString(parameters, "screen_size", deviceInfo.screenSize); + PackageBuilder.addLong(parameters, "session_count", activityStateCopy.sessionCount); + PackageBuilder.addDuration(parameters, "session_length", activityStateCopy.sessionLength); + PackageBuilder.addLong(parameters, "subsession_count", activityStateCopy.subsessionCount); + PackageBuilder.addDuration(parameters, "time_spent", activityStateCopy.timeSpent); + PackageBuilder.addString(parameters, "updated_at", deviceInfo.appUpdateTime); + + // purchase verification specific parameters + PackageBuilder.addString(parameters, "product_id", event.getProductId()); + PackageBuilder.addString(parameters, "purchase_token", event.getPurchaseToken()); + PackageBuilder.addString(parameters, "event_token", event.getEventToken()); + PackageBuilder.addString(parameters, "currency", event.getCurrency()); + PackageBuilder.addDouble(parameters, "revenue", event.getRevenue()); + + // google play games + PackageBuilder.addBoolean(parameters, "gpg_pc_enabled", deviceInfo.isGooglePlayGamesForPC ? true : null); + + injectFeatureFlagsWithParameters(parameters); + + checkDeviceIds(parameters); + return parameters; + } + private ActivityPackage getDefaultActivityPackage(ActivityKind activityKind) { ActivityPackage activityPackage = new ActivityPackage(activityKind); activityPackage.setClientSdk(deviceInfo.clientSdk); diff --git a/Adjust/test-app-core/src/main/java/com/adjust/testapp/AdjustCommandExecutor.java b/Adjust/test-app-core/src/main/java/com/adjust/testapp/AdjustCommandExecutor.java index fbc8cde77..3a1613ac8 100644 --- a/Adjust/test-app-core/src/main/java/com/adjust/testapp/AdjustCommandExecutor.java +++ b/Adjust/test-app-core/src/main/java/com/adjust/testapp/AdjustCommandExecutor.java @@ -18,7 +18,7 @@ import com.adjust.sdk.AdjustEventFailure; import com.adjust.sdk.AdjustEventSuccess; import com.adjust.sdk.AdjustPlayStoreSubscription; -import com.adjust.sdk.AdjustPurchase; +import com.adjust.sdk.AdjustPlayStorePurchase; import com.adjust.sdk.AdjustPurchaseVerificationResult; import com.adjust.sdk.AdjustSessionFailure; import com.adjust.sdk.AdjustSessionSuccess; @@ -793,8 +793,8 @@ private void verifyPurchase() { String purchaseToken = command.getFirstParameterValue("purchaseToken"); final String localBasePath = basePath; - AdjustPurchase purchase = new AdjustPurchase(sku, purchaseToken); - Adjust.verifyPurchase(purchase, new OnPurchaseVerificationFinishedListener() { + AdjustPlayStorePurchase purchase = new AdjustPlayStorePurchase(sku, purchaseToken); + Adjust.verifyPlayStorePurchase(purchase, new OnPurchaseVerificationFinishedListener() { @Override public void onVerificationFinished(AdjustPurchaseVerificationResult result) { MainActivity.testLibrary.addInfoToSend("verification_status", result.getVerificationStatus());