-
Notifications
You must be signed in to change notification settings - Fork 3
Callout required permissions for mParticle integration #694
base: main
Are you sure you want to change the base?
Conversation
When debugging a customer integration setup in mParticle, we discovered that Update profile permissions were inadequate, and that Create & Update profile permissions are preferred/default for integrations. https://docs.mparticle.com/guides/personalization/profiles/#input-protections
PreviewsSee contents
mParticle can be a useful integration tool for seeing all events and revenue that occur for your app even if it’s not active for a period of time. You can use mParticle to clean up your data infrastructure and collect new customer data, which can then be connected with other tools through mParticle’s powerful API. With our mParticle integration, you can:
With accurate and up-to-date subscription data in mParticle, you'll be set to turbocharge your campaigns ⚡️ Integration at a Glance
EventsThe mParticle integration tracks the following events: [block:parameters] For events that have revenue, such as trial conversions and renewals, RevenueCat will automatically record this amount along with the event in mParticle. Setup1. Set mParticle User Identity and Send Device Data to RevenueCatIf you're using the mParticle SDK, you can set the User ID to match the RevenueCat App User Id. This way, events sent from the mParticle SDK and events sent from RevenueCat can be synced to the same user. The mParticle integration also requires some device-specific data. RevenueCat will only send events into mParticle if the below Subscriber Attributes keys have been set for the device.
These properties can be set manually, like any other Subscriber Attributes, or through the helper methods Create an // Configure Purchases SDK
Purchases.configure(withAPIKey: "public_sdk_key", appUserID: "my_app_user_id")
// Set App User Id in mParticle
let options = MParticleOptions(key: "mParticle_app_key",
secret: "mParticle_app_secret")
let identityRequest = MPIdentityApiRequest.withEmptyUser()
identityRequest.email = "user@example.com"
identityRequest.customerId = "123456"
options.identifyRequest = identityRequest
options.onIdentifyComplete = { (result: MPIdentityApiResult?, error: Error?) in
guard error == nil else {
// handle error
return
}
guard let result else {
// handle empty result
return
}
// IMPORTANT: user identified successfully, get the mPID and send to RevenueCat
let mPid = result.user.userId
Purchases.shared.attribution.collectDeviceIdentifiers()
Purchases.shared.attribution.setMparticleID(mPid.stringValue)
}
// Start mParticle
MParticle.sharedInstance().start(with: options) // Configure Purchases SDK
[RCPurchases configureWithAPIKey:@"public_sdk_key" appUserID:@"my_app_user_id"];
// Set App User Id in mParticle
MParticleOptions *options = [MParticleOptions optionsWithKey:@"mParticle_app_key"
secret:@"mParticle_app_secret"];
MPIdentityApiRequest *identityRequest = [MPIdentityApiRequest requestWithEmptyUser];
identityRequest.email = @"user@example.com";
identityRequest.customerId = @"123456";
options.identifyRequest = identityRequest;
options.onIdentifyComplete = ^(MPIdentityApiResult *_Nullable apiResult, NSError *_Nullable error) {
if (error) {
// handle error
return;
}
if (apiResult == nil) {
// handle empty result
return;
}
// user identified successfully, get the mPID and send to RevenueCat
NSNumber *mPid = [apiResult.user userId];
[[RCPurchases sharedPurchases] collectDeviceIdentifiers];
[[RCPurchases sharedPurchases] setMparticleID:mPid.stringValue];
};
// Start mParticle
[[MParticle sharedInstance] startWithOptions:options]; // Configure Purchases SDK
Purchases.configure(this, "my_api_key", "my_app_user_id");
// Set App User Id in mParticle
IdentityApiRequest identityRequest = IdentityApiRequest.withEmptyUser()
.email("user@example.com")
.customerId("123456")
.build();
BaseIdentityTask identifyTask = new BaseIdentityTask()
.addFailureListener(new TaskFailureListener() {
@Override
public void onFailure(IdentityHttpResponse identityHttpResponse) {
// handle failure
}
}).addSuccessListener(new TaskSuccessListener() {
@Override
public void onSuccess(IdentityApiResult identityApiResult) {
// user identified successfully, get the mPID and send to RevenueCat
long mPid = identityApiResult.getUser().getId();
Purchases.getSharedInstance().collectDeviceIdentifiers();
Purchases.getSharedInstance().setMparticleID(String.valueOf(mPid));
}
});
MParticleOptions options = MParticleOptions.builder(this)
.credentials("mParticle_app_key", "mParticle_app_secret")
.identify(identityRequest)
.identifyTask(identifyTask)
.build();
// Start mParticle
MParticle.start(options); mParticle also allows you to log a user in after starting the SDK and log a user out; you should handle both of these cases: // handle logging out
MParticle.sharedInstance().identity.logout(completion: { (result: MPIdentityAPIResult?, error: Error?) in
Purchases.shared().reset()
}) // handle logging out
[[[MParticle sharedInstance] identity] logoutWithCompletion:^(MPIdentityApiResult *_Nullable apiResult, NSError *_Nullable error) {
[[RCPurchases sharedPurchases] reset];
}]; // handle logging out
MParticle.getInstance().Identity().logout(identityRequest)
.addFailureListener(new TaskFailureListener() {
@Override
public void onFailure(IdentityHttpResponse identityHttpResponse) {
// handle error
}
})
.addSuccessListener(new TaskSuccessListener() {
@Override
public void onSuccess(IdentityApiResult identityApiResult) {
Purchases.getSharedInstance().reset();
}
}); // handle logging in
MParticle.sharedInstance()
.identity.login(identityRequest, completion: { (result: MPIdentityAPIResult?, error: Error?) in
guard error == nil else {
// handle error
return
}
guard let result else {
// handle empty result
return
}
// user identified successfully, get the mPID and send to RevenueCat
let mPid = result.user.userId
Purchases.shared.attribution.collectDeviceIdentifiers()
Purchases.shared.attribution.setMparticleID(mPid.stringValue)
}) // handle logging in
[[[MParticle sharedInstance] identity] login:identityRequest
completion:^(MPIdentityApiResult *_Nullable apiResult, NSError *_Nullable error) {
if (error) {
// handle error
return;
}
if (apiResult == nil) {
// handle empty result
return;
}
// user identified successfully, get the mPID and send to RevenueCat
NSNumber *mPid = [apiResult.user userId];
[RCPurchases shared] collectDeviceIdentifiers];
[RCPurchases shared] setMparticleID: [mPid stringValue]];
}]; // handle logging in
MParticle.getInstance().Identity().login(identityRequest)
.addFailureListener(new TaskFailureListener() {
@Override
public void onFailure(IdentityHttpResponse identityHttpResponse) {
// handle error
}
})
.addSuccessListener(new TaskSuccessListener() {
@Override
public void onSuccess(IdentityApiResult identityApiResult) {
// user identified successfully, get the mPID and send to RevenueCat
long mPid = identityApiResult.getUser().getId();
Purchases.getSharedInstance().collectDeviceIdentifiers();
Purchases.getSharedInstance().setMparticleID(String.valueOf(mPid));
}
});
2. Add RevenueCat Feed Inputs in mParticleIn mParticle, add the RevenueCat feed input and create two feeds: one for the Android platform and one for the iOS platform. Copy each feed's Server to Server Key and Server to Server Secret for setup on RevenueCat. Refer to mParticle's documentation to learn more about feeds.
3. Send RevenueCat events to mParticleAfter you've set up the Purchases SDK and mParticle SDK to have the same user identity, you can "turn on" the integration and configure the event names from the RevenueCat dashboard.
4. Testing the mParticle integrationYou can test the mParticle integration end-to-end before going live. It's recommended that you test the integration is working properly for new users, and any existing users that may update their app to a new version. Make a sandbox purchase with a new userSimulate a new user installing your app, and go through your app flow to complete a sandbox purchase. Check that the required device data is collectedNavigate the the Customer View for the test user that just made a purchase. Make sure that all of the required data from step 1 above is listed as attributes for the user. Check that the mParticle event delivered successfullyWhile still on the Customer View, click into the test purchase event in the Customer History and make sure that the mParticle integration event exists and was delivered successfully.
Sample EventsBelow are sample JSONs that are delivered to mParticle for the different event types. {
"events": [
{
"data": {
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1640653937777,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "initial_purchase"
},
"product_action": {
"action": "purchase",
"transaction_id": "GPA.1234-5678-9012-34567",
"total_amount": 5.99,
"products": [
{
"id": "weekly_sub",
"price": 5.99,
"quantity": 1,
"total_product_amount": 5.99
}
]
},
"currency_code": "USD",
"is_non_interactive": false
},
"event_type": "commerce_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "GB",
"revenuecat_$ip": "123.45.67.89",
"revenuecat_$appsflyerId": "1234567890123-1234567890123456789",
"revenuecat_$onesignalId": "1234567890123-1234567890123456789",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "Android",
"android_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
"os": "Android"
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"event_name": "trial_started",
"custom_event_type": "transaction",
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1593506190000,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "trial_started"
}
},
"event_type": "custom_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "MD",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012",
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1593505191000,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "trial_converted"
},
"product_action": {
"action": "purchase",
"transaction_id": "123456789012345",
"total_amount": 5.99,
"products": [
{
"id": "weekly_sub",
"price": 5.99,
"quantity": 1,
"total_product_amount": 5.99
}
]
},
"currency_code": "USD",
"is_non_interactive": true
},
"event_type": "commerce_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "DE",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"event_name": "trial_cancelled",
"custom_event_type": "transaction",
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1593505162390,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "trial_cancelled"
}
},
"event_type": "custom_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "GB",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1593514136000,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "renewal"
},
"product_action": {
"action": "purchase",
"transaction_id": "123456789012345",
"total_amount": 5.99,
"products": [
{
"id": "weekly_sub",
"price": 5.99,
"quantity": 1,
"total_product_amount": 5.99
}
]
},
"currency_code": "USD",
"is_non_interactive": true
},
"event_type": "commerce_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "FR",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"event_name": "cancellation",
"custom_event_type": "transaction",
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1593507675551,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "cancellation"
}
},
"event_type": "custom_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "RU",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"event_name": "uncancellation",
"custom_event_type": "transaction",
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1657090180007,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "uncancellation"
}
},
"event_type": "custom_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "US",
"revenuecat_$ip": "123.45.67.89",
"revenuecat_$appsflyerId": "1234567890123-1234567890123456789",
"revenuecat_$onesignalId": "1234567890123-1234567890123456789",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
"os": "IOS"
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"event_name": "expiration",
"custom_event_type": "transaction",
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1643111704000,
"custom_attributes": {
"revenuecat_expiration_reason": "UNSUBSCRIBE",
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "expiration"
}
},
"event_type": "custom_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "AU",
"revenuecat_$ip": "123.45.67.89",
"revenuecat_$appsflyerId": "1234567890123-1234567890123456789",
"revenuecat_$onesignalId": "1234567890123-1234567890123456789",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
"os": "iOS"
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"event_name": "billing_issue",
"custom_event_type": "transaction",
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1643386961344,
"custom_attributes": {
"revenuecat_product_id": "weekly_sub",
"revenuecat_event": "billing_issue"
}
},
"event_type": "custom_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "US",
"revenuecat_$ip": "123.45.67.89",
"revenuecat_$appsflyerId": "1234567890123-1234567890123456789",
"revenuecat_$onesignalId": "1234567890123-1234567890123456789",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
"os": "iOS"
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} {
"events": [
{
"data": {
"event_name": "product_change",
"custom_event_type": "transaction",
"source_message_id": "12345678-1234-1234-1234-123456789012",
"timestamp_unixtime_ms": 1642376571000,
"custom_attributes": {
"revenuecat_new_product_id": "weekly_sub",
"revenuecat_product_id": "monthly_sub",
"revenuecat_event": "product_change"
}
},
"event_type": "custom_event"
}
],
"source_request_id": "12345678-1234-1234-1234-123456789012",
"user_attributes": {
"$country": "US",
"revenuecat_$ip": "123.45.67.89",
"revenuecat_$appsflyerId": "1234567890123-1234567890123456789",
"revenuecat_$onesignalId": "1234567890123-1234567890123456789",
"revenuecat_aliases": [
"12abc345d67e890fgh12j3lm456n7890"
],
"revenuecat_app_user_id": "123456789",
"revenuecat_original_app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c"
},
"device_info": {
"platform": "iOS",
"ios_advertising_id": "12345678-1234-1234-1234-123456789012"
},
"application_info": {
"application_name": "App_Name",
"package": "co.appname",
"os": "iOS"
},
"user_identities": {},
"schema_version": 2,
"environment": "production",
"mpid": "12345678"
} See contentsWhat is RevenueCat?RevenueCat provides a backend and a wrapper around StoreKit and Google Play Billing to make implementing in-app purchases and subscriptions easy. With our SDK, you can build and manage your app business on any platform without having to maintain IAP infrastructure. You can read more about how RevenueCat fits into your app or you can sign up free to start building. InstallationWe provide 2 ways to install our SDK: via Unity Package Manager (UPM) in the OpenUPM registry, or as a Option 1 (recommended): Install using OpenUPM
[block:callout] [block:callout] Configure a Main Gradle TemplateGo to Project -> Build Settings -> Player Settings -> Android tab -> Publishing Settings, and check "Custom Base Gradle Template", then close that window. Go to Assets -> External Dependency Manager -> Android Resolver -> Settings, then check "Patch mainTemplate.gradle" Option 2: Import the Purchases Unity packageDownload the latest version of Purchases.unitypackage. Import the downloaded unitypackage to your Unity project. Make sure the [block:callout] Configure a Main Gradle TemplateGo to Project -> Build Settings -> Player Settings -> Android tab -> Publishing Settings, and check "Custom Base Gradle Template", then close that window. Go to Assets -> External Dependency Manager -> Android Resolver -> Settings, then check "Patch mainTemplate.gradle" Create a GameObject with the Purchases behaviorThe Purchases package will include a MonoBehavior called Purchases. This will be your access point to RevenueCat from inside Unity. It should be instantiated once and kept as a singleton. You can use properties to configure your API Key, app user ID (if you have one), and product identifiers you want to fetch. Link StoreKit (iOS only)
Subclass Purchases.Listener MonoBehaviorThe Purchases behavior takes one additional parameter, a GameObject with a Purchases.Listener component. This will be where you handle purchase events, and updated subscriber information from RevenueCat. Here is a simple example: using System;
using System.Collections.Generic;
using UnityEngine;
public class PurchasesListener : Purchases.UpdatedCustomerInfoListener
{
public override void CustomerInfoReceived(Purchases.CustomerInfo customerInfo)
{
// display new CustomerInfo
}
private void Start()
{
var purchases = GetComponent<Purchases>();
purchases.SetDebugLogsEnabled(true);
purchases.GetOfferings((offerings, error) =>
{
if (error != null)
{
// show error
}
else
{
// show offering
}
});
}
public void BeginPurchase(Purchases.Package package)
{
var purchases = GetComponent<Purchases>();
purchases.PurchasePackage(package, (productIdentifier, customerInfo, userCancelled, error) =>
{
if (!userCancelled)
{
if (error != null)
{
// show error
}
else
{
// show updated Customer Info
}
}
else
{
// user cancelled, don't show an error
}
});
}
void RestoreClicked()
{
var purchases = GetComponent<Purchases>();
purchases.RestorePurchases((customerInfo, error) =>
{
if (error != null)
{
// show error
}
else
{
// show updated Customer Info
}
});
}
} Unity EditorRunning the Purchases SDK is unsupported in the Unity Editor at this time, and may result in Proguard rulesIf you have enabled Minify in Unity, make sure to add these custom rules to your
Installation with Unity IAP side by side[block:callout] Purchases Unity 5.0.0+side by side with Unity IAP 4.8.0 This version is only compatible with version 4.8.0 and above of Unity IAP which are the ones that include BillingClient 5. To install download If using RevenueCat alongside Unity IAP 2.2.0+ or other plugin that includes the Android BillingClient library you will be getting an error when compiling that warns about some BillingClient classes being duplicated. The easiest way to remove the error would be to tell Gradle to not include the billingclient library that Unity IAP is already including. In order to do that, make sure you have Modify the dependencies {
...
// ** ADD THIS **
configurations.all {
exclude group: 'com.android.billingclient', module: 'billing'
}
} Perform a clean up of the resolved dependencies using the Also make sure to perform a resolve, so External Dependency Manager adds the right dependencies to the generated [block:callout] Troubleshooting "ClassNotFoundException" errors at Runtime in AndroidWhen exporting your project to Android, in the Build Settings window, make sure you uncheck the Installing old versions of the pluginPurchases Unity 4.2.0+side by side with Unity IAP 4.4.0 < 4.8.0 Download Purchases Unity 4.0.0 and 4.1.0side by side with Unity IAP 3.3.0 < 4.4.0 Download Next Steps
|
https://docs.mparticle.com/guides/personalization/profiles/#input-protections
Motivation / Description
When debugging a customer integration setup in mParticle, we discovered that Update profile permissions were inadequate, and that Create & Update profile permissions are preferred/default for integrations.
We didn't outline this in our docs for mParticle setup.
Changes introduced
Callout to set permissions to Create & Update in mparticle docs
Linear ticket (if any)
Additional comments