Skip to content
This repository has been archived by the owner on Mar 22, 2024. It is now read-only.

Add new reserved subscriber attribute $onesignalUserId and add deprecation note to $onesignalId #408

Conversation

Raquel10-RevenueCat
Copy link
Contributor

Motivation / Description

We are migrating OneSignal API to User Centric v11.0. We are adding a new reserved attribute $onesignalUserId to allow developers to provide the user id required by the new API, and adding a note that the existing $onesignalId (subscription id) is deprecated for OneSignal APIs >v9.0.

Changes introduced

Linear ticket (if any)

Additional comments

Copy link
Contributor Author

@Raquel10-RevenueCat Raquel10-RevenueCat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linter error not related with this change and caused by this snippet:

  • /Users/distiller/project/code_blocks/🛠 Tools/paywalls/paywalls_4.swift

@Raquel10-RevenueCat Raquel10-RevenueCat marked this pull request as draft October 3, 2023 17:43
@NachoSoto
Copy link
Contributor

Lint fix: #410

Purchases.configure(withAPIKey: "<revenuecat_api_key>", appUserID: nil)

OneSignal.initialize("<onesignal_app_id>", withLaunchOptions: launchOptions)
OneSignal.add(self as OSSubscriptionObserver)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is required since AppDelegate conforms to OSSubscriptionObserver

Suggested change
OneSignal.add(self as OSSubscriptionObserver)
OneSignal.add(self)

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

Purchases.configure(withAPIKey: "<revenuecat_api_key>", appUserID: nil)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we try to not have this in quotes to force users to add the api key since that would fail to compile otherwise:

Suggested change
Purchases.configure(withAPIKey: "<revenuecat_api_key>", appUserID: nil)
Purchases.configure(withAPIKey: <revenuecat_api_key>, appUserID: nil)

@RCGitBot
Copy link
Contributor

Previews

temp/android-products.md

See contents

To set up products for Android devices, start by logging into Google Play Console. Google Play Console is Google's central hub for managing app releases, testing, in-app purchases, and more.

This guide assumes basic knowledge of Google Play Console, as well as having an app set up and ready for adding in-app purchases. For more information, visit Google's documentation and guides for Google Play Console.

Create an In-App Product or Subscription

📘

You'll need to have an APK uploaded before you can create in-app products. Check out our guide on sandbox testing on Android for details on how to upload an APK and roll out a release on a closed test track.

To create an in-app product or subscription, go to Google Play Console's 'All Applications' page and select your app from the list.

In the sidebar, select the Products dropdown. Depending on your in-app product type, you will either choose In-app products or Subscriptions.

After clicking Create, provide a couple pieces of metadata to Google:

Metadata Description
Product ID The product ID is a unique alphanumeric ID that is used for accessing your product in development and syncing with RevenueCat. After you use a Product ID for one product in Google Play Console, it can’t be used again across any of your apps, even if the product is deleted.
Name A short name of the item, up to 55 characters. This will be displayed on your Google Play Store listing.

Tips for creating robust product IDs

After you use a Product ID for one product in Google Play Console, it can’t be used again across any of your apps, even if the product is deleted. It helps to be a little organized here from the beginning - we recommend using a consistent naming scheme across all of your product identifiers such as:

<app>_<entitlement>_<version>

  • app: Some prefix that will be unique to your app, since the same product Id cannot but used in any future apps you create.
  • entitlement: A name for what the product provides access to, e.g., "premium"
  • version: A version number

For example, using this format the identifier for your first product that grants access to a "premium" subscription would be:

rc_premium_v1

Create a base plan

For subscription products, you'll need to add a base plan. Base plans define a billing period, price, and renewal type for purchasing your subscription. Customers never purchase a subscription product directly, they always purchase a base plan of a subscription.

Click "Add base plan" and fill out the associated fields. Make sure to set a price, and click "Activate". Since Google introduced multiple base plans with Billing Client 5, it's good practice to be as clear as possible when naming your plans, such as: <duration>-<renewaltype>, eg. annual-autorenewing.

Screenshot 2023-07-27 at 4 56 24 PM

[block:callout]
{
"type": "success",
"title": "Migrated products from before May 2022",
"body": "When Google introduced the new subscription features in May 2022, all existing subscriptions were migrated to subscription products with a single base plan. That base plan has an identifier representing the duration, like P1Y which stands for annual."
}
[/block]

[block:callout]
{
"type": "info",
"title": "Representation of Google Play subscription products in RevenueCat",
"body": "RevenueCat Products map to Base Plans for Google Play subscriptions, since those are the products that customers can purchase. Newly set up products in RevenueCat follow the identifier format <subscription_id>:<base-plan-id>, whereas products that were set up before February 2023 follow the identifier format <subscription_id>."
}
[/block]

[block:callout]
{
"type": "danger",
"title": "Support for non backwards-compatible base plans",
"body": "Old versions of RevenueCat SDKs do not support Google's new subscription features such as multiple base plans per subscription product. Only base plans marked as "backwards compatible" in Google Play Console are available in these SDK versions. Learn more. Only one base plan per subscription can be marked as backwards compatible."
}
[/block]
To mark a base plan as backwards compatible, click the overflow menu on the base plan and select "Use for deprecated billing methods".

(Optional) Create an offer

If you wish to create an offer for your base plan, you can do so from the subscription page by clicking "Add offer". Offers can be free trials, discounts, or simply special price setups that apply when a customer first purchases a subscription.

You can then select a product ID, eligibility, and offer phases.
[block:callout]
{
"type": "danger",
"title": "Support for non backwards-compatible offers",
"body": "Old versions of RevenueCat SDKs do not support Google's new subscription features such as multiple offers per base plan. Only offers marked as "backwards compatible" in Google Play Console are available in these SDK versions. Learn more. Only one offer per base plan can be marked as backwards compatible."
}
[/block]
To mark an offer as backwards compatible, click the overflow menu and select "Use for deprecated billing methods".

Making Subscriptions Editable, InAppProduct API

RevenueCat does not use the InAppProduct API for subscriptions. You are safe to make subscriptions editable, unless you are manually using this API outside of RevenueCat.

This is related to this notice:

If you are relying solely on RevenueCat for your subscriptions, you can safely select "Make editable".

Integrate with RevenueCat

If you're ready to integrate your new Google Play in-app product with RevenueCat, continue our product setup guide .

temp/onesignal.md

See contents

👍

The OneSignal integration is available to all users signed up after September '23, the legacy Grow and Pro plans, and Enterprise plans. If you're on a legacy Free or Starter plan and want to access this integration, migrate to our new pricing via your billing settings.

OneSignal can be a useful integration tool for understanding what stage a customer is in to react accordingly. RevenueCat can automatically update user tags in OneSignal with their latest subscription status.

With our OneSignal integration, you can:

  • Send an onboarding campaign to a user in a free trial
  • Send a push notification to churned users and offer them a discount

With accurate and up-to-date subscription data in OneSignal, you'll be set to turbocharge your campaigns ⚡️

For every auto-renewing subscription event in RevenueCat, the following tags get added or updated on the user in OneSignal. By leaving the tag blank in the RevenueCat dashboard, you can choose to not send any value for specific tag(s).

[block:parameters]
{
"data": {
"h-0": "Tag",
"h-1": "Description",
"0-0": "app_user_id",
"0-1": "The RevenueCat App User Id that triggered the event",
"1-0": "period_type",
"1-1": "The latest period type for the purchase or renewal. Either: \n- TRIAL (for free trials) \n- INTRO (or introductory pricing) \n- NORMAL (standard subscription)",
"2-0": "purchased_at",
"2-1": "epoch time in seconds of the latest subscription purchase or renewal",
"3-0": "expiration_at",
"3-1": "epoch time in seconds of the latest subscription expiration date",
"4-0": "store",
"4-1": "Either APP_STORE , PLAY_STORE, or STRIPE",
"5-0": "environment",
"5-1": "Either SANDBOX or PRODUCTION",
"6-0": "last_event_type",
"6-1": "The latest event type from the user. Either: \n- INITIAL_PURCHASE \n- TRIAL_STARTED \n- TRIAL_CONVERTED \n- TRIAL_CANCELLED \n- RENEWAL \n- CANCELLATION ",
"7-0": "product_id",
"7-1": "The latest subscription product identifier that the user has purchased or renewed",
"8-0": "entitlement_ids",
"8-1": "Comma separated string of RevenueCat Entitlement identifiers that the user unlocked",
"9-0": "active_subscription",
"9-1": "The value will be set to true on any purchase/renewal event, and false on EXPIRATION",
"10-0": "grace_period_expiration_at",
"10-1": "If a billing issue occurs we will send the date of the grace period expiration."
},
"cols": 2,
"rows": 11,
"align": [
"left",
"left"
]
}
[/block]

📘 Auto-renewing subscriptions only

RevenueCat only updates data tags in OneSignal in response to auto-renewing subscription events.

Integration at a Glance

Includes Revenue Supports Negative Revenue Sends Sandbox Events Includes Subscriber Attributes Sends Transfer Events Optional Event Types

Events

The OneSignal integration tracks the following events:

[block:parameters]
{
"data": {
"h-0": "Event",
"h-1": "Default Event Name",
"h-2": "Description",
"h-3": "App Store",
"h-4": "Play Store",
"h-5": "Amazon",
"h-6": "Stripe",
"h-7": "Promo",
"0-0": "Initial Purchase",
"0-1": "initial_purchase",
"0-2": "A new subscription has been purchased.",
"0-3": "✅",
"0-4": "✅",
"0-5": "✅",
"0-6": "✅",
"0-7": "❌",
"1-0": "Trial Started",
"1-1": "trial_started",
"1-2": "The start of an auto-renewing subscription product free trial.",
"1-3": "✅",
"1-4": "✅",
"1-5": "✅",
"1-6": "✅",
"1-7": "❌",
"2-0": "Trial Converted",
"2-1": "trial_converted",
"2-2": "When an auto-renewing subscription product converts from a free trial to normal paid period.",
"2-3": "✅",
"2-4": "✅",
"2-5": "✅",
"2-6": "✅",
"2-7": "❌",
"3-0": "Trial Cancelled",
"3-1": "trial_cancelled",
"3-2": "When a user turns off renewals for an auto-renewing subscription product during a free trial period.",
"3-3": "✅",
"3-4": "✅",
"3-5": "✅",
"3-6": "✅",
"3-7": "❌",
"4-0": "Renewal",
"4-1": "renewal",
"4-2": "An existing subscription has been renewed or a lapsed user has resubscribed.",
"4-3": "✅",
"4-4": "✅",
"4-5": "✅",
"4-6": "✅",
"4-7": "❌",
"5-0": "Cancellation",
"5-1": "cancellation",
"5-2": "A subscription or non-renewing purchase has been cancelled. See cancellation reasons for more details.",
"5-3": "✅",
"5-4": "✅",
"5-5": "✅",
"5-6": "✅",
"5-7": "✅",
"6-0": "Uncancellation",
"6-1": "uncancellation",
"6-2": "A non-expired cancelled subscription has been re-enabled.",
"6-3": "✅",
"6-4": "✅",
"6-5": "✅",
"6-6": "❌",
"6-7": "❌",
"7-0": "Non Subscription Purchase",
"7-1": "non_subscription_purchase",
"7-2": "A customer has made a purchase that will not auto-renew.",
"7-3": "✅",
"7-4": "✅",
"7-5": "✅",
"7-6": "✅",
"7-7": "✅",
"8-0": "Subscription paused",
"8-1": "subscription_paused",
"8-2": "A subscription has been paused.",
"8-3": "❌",
"8-4": "✅",
"8-5": "❌",
"8-6": "❌",
"8-7": "❌",
"9-0": "Expiration",
"9-1": "expiration",
"9-2": "A subscription has expired and access should be removed. \n \nIf you have Platform Server Notifications configured, this event will occur as soon as we are notified (within seconds to minutes) of the expiration. \n \nIf you do not have notifications configured, delays may be approximately 1 hour.",
"9-3": "✅",
"9-4": "✅",
"9-5": "✅",
"9-6": "✅",
"9-7": "✅",
"10-0": "Billing Issue",
"10-1": "billing_issue",
"10-2": "There has been a problem trying to charge the subscriber. This does not mean the subscription has expired. \n \nCan be safely ignored if listening to CANCELLATION event + cancel_reason=BILLING_ERROR.",
"10-3": "✅",
"10-4": "✅",
"10-5": "✅",
"10-6": "✅",
"10-7": "❌",
"11-0": "Product Change",
"11-1": "product_change",
"11-2": "A subscriber has changed the product of their subscription. \n \nThis does not mean the new subscription is in effect immediately. See Managing Subscriptions for more details on updates, downgrades, and crossgrades.",
"11-3": "✅",
"11-4": "✅",
"11-5": "❌",
"11-6": "✅",
"11-7": "❌"
},
"cols": 8,
"rows": 12,
"align": [
"left",
"left",
"left",
"left",
"left",
"left",
"left",
"left"
]
}
[/block]

1. Send device data to RevenueCat

OneSignal API versions v9.0 and below (deprecated)

The OneSignal integration requires some device-specific data. RevenueCat will only update users in OneSignal if the below data has been added as Subscriber Attributes for the user.

Key Description Required
$onesignalId The OneSignal Player Id for the user.

This property can be set manually, like any other Subscriber Attributes, or through the helper method setOnesignalID().

You can listen for changes to the OneSignal Id through their SDK, and send the value to RevenueCat. If you already have OneSignal set up, you should make sure that you're also sending the OneSignal Id for users that are updating to the latest version of your app.

class AppDelegate: UIResponder, UIApplicationDelegate, OSSubscriptionObserver {

    var window: UIWindow?

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        Purchases.configure(withAPIKey: "<revenuecat_api_key>", appUserID: nil)

        OneSignal.initWithLaunchOptions(launchOptions, appId: "<onesignal_app_id>")
        OneSignal.add(self as OSSubscriptionObserver)

        // If you've already set up OneSignal, then users should already have
        // a OneSignal Id. We can check if it's available and send it to RevenueCat
        if let onesignalId = OneSignal.getUserDevice()?.getUserId() {
            Purchases.shared.attribution.setOnesignalID(onesignalId)
        }

        return true
    }

    // Add this method to update the $onesignalId in RevenueCat whenever it changes
    // This code should be sufficient to capture all new users if you're setting
    // up OneSignal for the first time.
    func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges!) {
        if !stateChanges.from.subscribed && stateChanges.to.subscribed {
            // The user is subscribed
            // Either the user subscribed for the first time
            Purchases.shared.attribution.setOnesignalID(stateChanges.to.userId)
        }
    }
}

OneSignal API versions v11.0 and above

The OneSignal integration requires some user-specific data. RevenueCat will only update users in OneSignal if the below data has been added as Subscriber Attributes for the user.

Key Description Required
$onesignalUserId The OneSignal ID for the user.

This property can be set manually, like any other Subscriber Attributes, or through the helper method setOnesignalUserID().

You can listen for changes to the OneSignal User ID through their SDK, and send the value to RevenueCat. If you already have OneSignal set up, you should make sure that you're also sending the OneSignal ID for users that are updating to the latest version of your app.

class AppDelegate: UIResponder, UIApplicationDelegate, OSSubscriptionObserver {

    var window: UIWindow?

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        Purchases.configure(withAPIKey: "<revenuecat_api_key>", appUserID: nil)

        OneSignal.initialize("<onesignal_app_id>", withLaunchOptions: launchOptions)
        OneSignal.add(self as OSSubscriptionObserver)

        // If you've already set up OneSignal, then users should already have
        // a OneSignal Id. We can check if it's available and send it to RevenueCat
        if let onesignalUserId = OneSignal.User.onesignal_id {
            Purchases.shared.attribution.setOnesignalUserID(onesignalUserId)
        }

        return true
    }

    // Add this method to update the $onesignalUserId in RevenueCat whenever it changes
    // This code should be sufficient to capture all new users if you're setting
    // up OneSignal for the first time.
    func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges!) {
        if !stateChanges.from.subscribed && stateChanges.to.subscribed {
            // The user is subscribed
            // Either the user subscribed for the first time
            Purchases.shared.attribution.setOnesignalUserID(stateChanges.to.userId)
        }
    }
}

2. Send RevenueCat events into OneSignal

After you've set up the Purchases SDK to send device data to RevenueCat, you can "turn on" the integration and configure the tag names from the RevenueCat dashboard.

  1. Navigate to your project in the RevenueCat dashboard and find the Integrations card in the left menu. Select + New

  1. Choose OneSignal from the Integrations menu.
  2. Add your OneSignal App Id and OneSignal API key.
  3. Enter the tag names that RevenueCat should use, or choose the default tag names.

[block:image]
{
"images": [
{
"image": [
"https://files.readme.io/319d5b4-app.revenuecat.com_projects_85ff18c7_integrations_intercom_2.png",
null,
"OneSignal configuration screen"
],
"align": "center",
"caption": "OneSignal configuration screen"
}
]
}
[/block]

3. Testing the OneSignal integration

You can test the OneSignal 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 user

Simulate a new user installing your app, and go through your app flow to complete a sandbox purchase.

Check that the required device data is collected

Navigate 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 OneSignal event delivered successfully

While still on the Customer View, click into the test purchase event in the Customer History and make sure that the OneSignal integration event exists and was delivered successfully.

Sample Events

Below are sample JSONs that are delivered to OneSignal for events.

{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "NORMAL",
        "purchased_at": 1600016247,
        "expiration_at": 1602608247,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "initial_purchase",
        "last_event_at": 1600016250,
        "product_id": "monthly_sub",
        "entitlement_ids": "Pro"
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "TRIAL",
        "purchased_at": 1600031584,
        "expiration_at": 1600290784,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "trial_started",
        "last_event_at": 1600031586,
        "product_id": "three_month_sub_trial",
        "entitlement_ids": "Pro"
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "NORMAL",
        "purchased_at": 1602136340,
        "expiration_at": 1602741140,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "trial_converted",
        "last_event_at": 1602114850,
        "product_id": "weekly_sub_trial",
        "entitlement_ids": null
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "TRIAL",
        "purchased_at": 1602051920,
        "expiration_at": 1602311120,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "trial_cancelled",
        "last_event_at": 1602129368,
        "product_id": "weekly_sub_trial",
        "entitlement_ids": null
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "NORMAL",
        "purchased_at": 1602125078,
        "expiration_at": 1604807078,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "renewal",
        "last_event_at": 1602122793,
        "product_id": "monthly_sub",
        "entitlement_ids": "Pro"
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "NORMAL",
        "purchased_at": 1602086660,
        "expiration_at": 1602691460,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "cancellation",
        "last_event_at": 1602118600,
        "product_id": "weekly_sub",
        "entitlement_ids": null
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "TRIAL",
        "purchased_at": 1663445025,
        "expiration_at": 1664049825,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "uncancellation",
        "last_event_at": 1663969096,
        "product_id": "annual_sub",
        "entitlement_ids": "Premium"
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "purchased_at": 1602086660,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "non_renewing_purchase",
        "last_event_at": 1602118600,
        "product_id": "one_time_purchase_product",
        "entitlement_ids": null
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "NORMAL",
        "purchased_at": 1602086660,
        "expiration_at": 1602691460,
        "store": "PLAY_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "subscription_paused",
        "last_event_at": 1602118600,
        "product_id": "weekly_sub",
        "auto_resume_at": 1602119600,
        "entitlement_ids": null
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "period_type": "NORMAL",
        "purchased_at": 1652374230,
        "expiration_at": 1652979030,
        "last_event_type": "expiration",
        "last_event_at": 1652988735
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "TRIAL",
        "purchased_at": 1652383957,
        "expiration_at": 1654371157,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "billing_issue",
        "last_event_at": 1652988776,
        "product_id": "annual_sub",
        "entitlement_ids": "Premium"
    }
}
{
    "app_id": "12345678-1234-1234-1234-123456789012",
    "tags": {
        "app_user_id": "$RCAnonymousID:87c6049c58069238dce29853916d624c",
        "period_type": "NORMAL",
        "purchased_at": 1602086660,
        "expiration_at": 1602691460,
        "store": "APP_STORE",
        "environment": "PRODUCTION",
        "last_event_type": "product_change",
        "last_event_at": 1602118600,
        "product_id": "weekly_sub",
        "new_product_id": "monthly_sub",
        "entitlement_ids": null
    }
}

🚧 Why are tags not working?

If your tags aren't working and RevenueCat is sending events successfully with 200 codes, check out OneSignal's troubleshooting guide.

👍 You've done it!

You should start seeing subscription data from RevenueCat appear on users in OneSignal.

Subscription Status Attribute

Whenever RevenueCat sends an event to OneSignal, we'll send a subscription_status user attribute with any applicable changes, using one of the following values:

Status Description
active The customer has an active, paid subscription which is set to renew at their next renewal date.
intro The customer has an active, paid subscription through a paid introductory offer.
cancelled The customer has a paid subscription which is set to expire at their next renewal date.
grace_period The customer has a paid subscription which has entered a grace period after failing to renew successfully.
trial The customer is in a trial period which is set to convert to paid at the end of their trial period.
cancelled_trial The customer is in a trial period which is set to expire at the end of their trial period.
grace_period_trial The customer was in a trial period and has now entered a grace period after failing to renew successfully.
expired The customer's subscription has expired.
promotional The customer has access to an entitlement through a granted RevenueCat promotional subscription.
expired_promotional The customer previously had access to an entitlement through a granted RevenueCat promotional subscription that has since expired.
paused The customer has a paid subscription which has been paused and is set to resume at some future date.

For customers with multiple active subscriptions, this attribute will represent the status of only the subscription for which the most recent event occurred.

Please note that since this attribute is set and updated when events are delivered, subscribers with events prior to our release of this attribute (during November 2023) will not have this attribute set until/unless a future event (renewal, cancellation, etc) occurs.

temp/subscriber-attributes.md

See contents

Subscriber attributes are useful for storing additional, structured information on a user. For example, you could store your user's email address and additional system identifiers directly in RevenueCat. Attributes will not be seen by your users unless you choose to explicitly show them yourself.

📘

Subscriber attributes are only synced with RevenueCat servers when Purchases.configure() is called, app backgrounded, and when purchases are made or restored.

Setting Attributes

Subscriber attributes can be set through the SDK by passing a dictionary of strings to the setAttributes() method on the shared Purchases instance.

Purchases.shared.attribution.setAttributes(["age": "24",
                                            "custom_group_id": "abc123"])
[RCPurchases.sharedPurchases.attribution setAttributes:@{
    @"age": @"24",
    @"custom_group_id": @"abc123"
}];
Purchases.sharedInstance.setAttributes(mapOf("age" to "24", "custom_group_id" to "abc123"))
Map<String, String> attributes = new HashMap<String, String>();
attributes.put("age", "24");
attributes.put("custom_group_id", "abc123");

Purchases.getSharedInstance().setAttributes(attributes);
Purchases.setAttributes({ "age" : "24", "custom_group_id" : "abc123" });
Purchases.setAttributes({ "age" : "24", "custom_group_id" : "abc123" });
var purchases = GetComponent<Purchases>();
var attributes = new Dictionary<string, string>
{
    { "age", "24" }, 
    { "custom_group_id", "abc123" }
};
purchases.SetAttributes(attributes);

🚧

Since subscriber attributes are writable using a public key they should not be used for managing secure or sensitive information such as subscription status, coins, etc.

Restrictions

You can specify up to 50 attributes, with key names up to 40 characters long and values up to 500 characters long. Keys cannot start with $ unless it's for one of the reserved attributes below.

Attribute key checklist:
✅ Key does not contain whitespace
✅ Key must start with a letter for non-reserved attributes or "$" for reserved attributes
✅ Key does not include any non-alphanumeric characters except - and _
✅ Key is not more than 40 characters
✅ Value is not more than 500 characters
✅ No more than 50 custom attributes

Reserved attributes

Attribute keys beginning with $ are reserved for RevenueCat. The current list of reserved keys are below:

General

Key Description
$displayName Name that should be used to reference the user
$apnsTokens Apple push notification tokens for the user.
$fcmTokens Google push notification tokens for the user.
$attConsentStatus Apple App Tracking Transparency consent status for the user.
$ipAddress Ip Address for the user.
$clevertapId Clever Tap ID for the user.
$idfa iOS advertising identifier UUID.
$idfv iOS vender identifier UUID.
$gpsAdId The advertising ID that is provided by Google Play services.
$androidId Android device identifier.
$amazonAdId Amazon Advertising ID.
$adjustId The unique Adjust identifier for the user.
$amplitudeDeviceId The Amplitude Device ID.
$amplitudeUserId The Amplitude User ID.
$appsflyerId Appsflyer Id. The unique Appsflyer identifier for the user.
$brazeAliasName The Braze 'alias_name' in User Alias Object.
$brazeAliasLabel The Braze 'alias_label' in User Alias Object.
$clevertapId The CleverTap ID for the user.
$fbAnonId The Facebook Anonymous ID for the user.
$attConsentStatus Apple App Tracking Transparency consent status for the user.
$mparticleId The unique mParticle user identifier (mpid).
$onesignalId The OneSignal Player Id for the user. Deprecated for OneSignal versions >v9.0.
$onesignalUserId The OneSignal User Id for the user. Required with OneSignal versions >=v11.0.
$airshipChannelId The Airship channel ID for the user.
$iterableUserId The Iterable ID for the user.
$iterableCampaignId The Iterable campaign ID.
$iterableTemplateId The Iterable template ID.
$firebaseAppInstanceId The Firebase instance identifier.
$mixpanelDistinctId The Mixpanel user identifier.
$ip The IP address of the device.
$email Email address for the user.
$phoneNumber Phone number for the user.

🚧 attConsentStatus is populated regardless of requesting any permission

The RevenueCat SDK sends the current ATT status for the $attConsentStatus subscriber attribute regardless of if you are or aren't requesting any ATT permission. So just as a heads-up, you can expect to see this attribute filled.

Note: The RevenueCat SDK reads the current App Tracking Transparency Consent Status for the user, but will not modify it or request for further permission.

You may see the following as a response from this attribute:

  • restricted - Can be returned if the user is using a mobile device management profile that disallows some aspects of tracking regardless of consent. This might be returned even if you never ask for permissions.
  • denied - Can be returned if the user’s phone has set “Ask Apps Not To Track” in OS Settings or denied access for the specific app.
  • accepted - Returned if you ask for permission and the permission gets accepted by the user.
  • unknown - The user hasn’t set “Ask Apps Not to Track” in OS Settings, and you have never asked the user for consent to track activity.

Device Identifiers

Key Description
$idfa Apple advertising identifier
$idfv Apple vendor identifier
$gpsAdId Google advertising identifier
$androidId Android device identifier
$ip IP Address

📘 Device identifiers can't be changed once set

Once a device identifier is set for a subscriber, it can't be changed in order to keep these identifiers associated with the original installation. This allows RevenueCat to send events generated by a particular device to downstream integrations with a consistent identifier unaffected by uninstalls and reinstalls.

Third-party Identifiers

Key Description
$adjustId Adjust user identifier
$amplitudeDeviceId Amplitude device identifier
$amplitudeUserId Amplitude user identifier
$appsflyerId Appsflyer user identifier
$fbAnonId Facebook SDK anonymous user identifier
$firebaseAppInstanceId Firebase instance identifier
$iterableUserId Iterable user identifier
$mixpanelDistinctId Mixpanel user identifier
$mparticleId mParticle user identifier
$onesignalId OneSignal player identifier
$onesignalUserId OneSignal user identifier
$clevertapId CleverTap user identifier
$airshipChannelId Airship channel identifier

Braze User Alias Object

Key Description
$brazeAliasName Braze 'alias_name' in User Alias Object
$brazeAliasLabel Braze 'alias_label' in User Alias Object

Iterable Data

Key
$iterableCampaignId
$iterableTemplateId

Attribution Data

Key
$mediaSource
$campaign
$adGroup
$ad
$keyword
$creative

📘

If you have access to install attribution data, you can set it using the reserved keys above. RevenueCat itself is not an attribution network and can not automatically populate this information.

Once attribution data is set for a subscriber, it can't be changed. This way attribution data can be associated with the original installation without getting overwritten.

Reserved attributes can be written directly by setting the key (don't forget the $ prefix) or with special helper methods:

Purchases.shared.attribution.setEmail("test@example.com")
Purchases.shared.attribution.setPhoneNumber("+16505551234")
Purchases.shared.attribution.setDisplayName("John Appleseed")
[RCPurchases.sharedPurchases.attribution setEmail:@"test@example.com"];
[RCPurchases.sharedPurchases.attribution setPhoneNumber:@"+16505551234"];
[RCPurchases.sharedPurchases.attribution setDisplayName:@"John Appleseed"];
Purchases.sharedInstance.setEmail("test@example.com")
Purchases.sharedInstance.setPhoneNumber("+16505551234")
Purchases.sharedInstance.setDisplayName("John Appleseed")
Purchases.setEmail("test@example.com")
Purchases.setPhoneNumber("+16505551234")
Purchases.setDisplayName("John Appleseed")
Purchases.setEmail("test@example.com")
Purchases.setPhoneNumber("+16505551234")
Purchases.setDisplayName("John Appleseed")
var purchases = GetComponent<Purchases>();
purchases.SetEmail("asdf@asdfa.com");
purchases.SetPhoneNumber("asdga");
purchases.SetDisplayName("asdgas");

Setting push tokens

Push tokens can be used to engage with your users through Apple apns or Google cloud messaging. These can be saved in RevenueCat through system callbacks after the user accepts the push notification permissions in your app.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Purchases.shared.attribution.setPushToken(deviceToken)
}
- (void)application:(UIApplication *)application 
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
    [RCPurchases.sharedPurchases.attribution setPushToken:deviceToken];
}
Purchases.sharedInstance.setPushToken(deviceToken)
Purchases.setPushToken(deviceToken);
Purchases.setPushToken(deviceToken)
var purchases = GetComponent<Purchases>();
purchases.SetPushToken(deviceToken);

Deleting Attributes

Any attribute can be cleared by passing null or an empty string as the key value. Individual attributes can also be cleared for a specific user in their customer view.

Purchases.shared.attribution.setAttributes(["age": ""])
[RCPurchases.sharedPurchases.attribution setAttributes:@{@"age": @""}];
Purchases.sharedInstance.setAttributes(mapOf("age" to ""))
Purchases.setAttributes({"age" : ""});
Purchases.setAttributes({"age" : ""})
var purchases = GetComponent<Purchases>();
var attributes = new Dictionary<string, string>
{
    { "age", "" }, 
    { "custom_group_id", "" }
};
purchases.SetAttributes(attributes);

Reading Attributes

You can access subscriber attributes through the REST API using a secret key, in webhooks, and through analytics integrations (Amplitude, Mixpanel, Segment). The customer view dashboard will also show a list of attributes for the individual user that you can edit.

📘

Subscriber attributes are write-only from the SDK. Reading attributes should only be done server-side through the webhooks or REST API.

Subscriber attributes are also included with transaction data for Scheduled Data Exports.

Next Steps

  • Enrich your app by reacting to the user's current subscription status

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants