Understanding Transferred Events and Accounts

App accounts, login, logout, and transferred events.

Understanding exactly what events should be produced when working with app accounts can be a little tricky. This guide will help you understand what happens on the Nami platform and provide some insight on how to validate correct behavior.

There are 2 types of users that currently exist in the Nami platform.

  • Anonymous - the user has no external identifier
  • User with an app account - has an external identifier

Further, both of these types of users may or may not have any devices associated with the user.

The Nami user and external identifier are stored locally in the SDK. The correct user and entitlement state are fetched for the device each time the app comes to the foreground.

The occurrence of any of the events listed below will immediately update the user the device belongs to, whether there is a linked app account via an external identifier and the set of active entitlements on the device.

  • Server notification received - Upon receipt, the Nami platform will check if there is an existing user for this notification. If there is, that user's state will be updated. If there is not, an anonymous user will be created that is not linked to any device.
  • setExternalIdentifier called - tells Nami that there is an app account for this device. Device will be associated with the correct user.
  • clearExternalIdentifier called - removes the app account from the device. The device is now associated with an anonymous user.
  • App deleted from device - this clears the external identifier and device identifier. On the next app install, this device will be a new device on the Nami platform with no external identifier.
  • Purchase validation - if the device has any valid purchases, they will be processed at the start of any app session. This will grant the entitlement and assign it to whichever Nami user the device is currently assigned to.

Any combination of these events may trigger a transfer event depending on the state of the device from the combination of all events that have occurred prior on the device as well as the state of the different user objects in the Nami platform.

Common Scenarios

There are some common scenarios that can result in an active entitlement transferring from one user to another.

We'll walk through some of these examples to help you understand what should happen.

Scenario: Purchase before App Install

Some purchase platforms like Apple's App Store or the Google Play Store allow the user to purchase on the store before they have installed the app. A common example of this (but not the only one) is when the user redeems a promo code provided as a link. This promo link will initiate a purchase and then download and install the app to the device.

What happens in this scenario?

Most store platforms process the purchase immediately on app launch. If your app has an account creation flow, this means the purchase is made before the app account has been created.

Scenario: Server notification received first

The events produced by Apple's Server to Server notifications and Google's Real-Time Developer Notifications provide the Nami Platform details on a purchase. These events are produced asynchronously from the purchase events generated by the Nami SDKs.

If these events are received and processed before the Nami SDK events you may see some transferred events. Further, depending on when the setExternalIdentifier call happens there may be extra steps as there may be an anonymous user without a device and then an anonymous user with a device.

Scenario: Purchased user logs outs

In this scenario, a customer has made a purchase and it is linked to their app account. At some point, the customer signs out of the app.

The app and device will now belong to an anonymous user.

Scenario: App with active purchase and app account deleted and reinstalled

In this scenario, an app account was created with setExternalIdentifier and then a purchase was made and the user has an active entitlement.

The user then deletes the app. When the app is reinstalled, setExternalIdentifier is not called.

In this case, the entitlement is transferred to an anonymous user.

Scenario: User with active purchase signs out of 1 account and signs into a 2nd account

In this situation, the user has 2 app accounts. They've made a purchase on device. They sign out of the first out and then sign into the 2nd account. As part of our fraud prevention, we ensure only the 2nd account has access to the purchase.

Validating Account State and Purchases in Testing and Development

If you are seeing unexpected transfer events or purchases not being associated with your external identifier, you should look at when you are calling setExternalIdentifier or clearExternalIdentifier.

Most likely the issue is that in one of the common scenarios above, these calls are not being made at the right time for the external identifier to be set correctly when the purchase is processed and the entitlement is granted.

Below are some suggested testing methods to help validate what your external identifier is at the time a purchase is made.

Note that these tests are focused on the initial purchase. If one of the other scenarios described above is the reason for the transferred events, you'll need to carefully step through any sign-in and sign-out scenarios in your app flow when there is an active purchase on the device.

Delete your App

Once an external identifier has been set, it is stored locally on device until:

  • You call clearExternalIdentifier
  • The app is deleted

When testing behaviors around the timing of the setExternalIdentifier call, we recommend deleting the app so there is no saved state so you can see when the identifier is being set when the app is first run after install.

Check the External Identifier at Purchase Time

As early as possible in your app's execution, register a callback to react to changes in purchases (or modify the one you already have) to print out the current external identifier at this point.

Here's an example that will print the external identifier at the start and completion of the purchase process.

NamiPurchaseManager.registerPurchasesChangedHandler { (purchases: [NamiPurchase], purchaseState: NamiPurchaseState, error: Error) in
  let external_id = Nami.getExternalIdentifier()
  if purchaseState == .pending {
    // purchase process has started but not finished
    print("external id: " + (ex_id ?? "None"))
  } else if purchaseState == .purchased {
    // purchase completed successfully
    print("external id: " + (ex_id ?? "None"))
  }
}
NamiPurchaseManager.registerPurchasesChangedListener { namiPurchases, namiPurchaseState, error ->
  val ex_id = Nami.getExternalIdentifier()
  when (namiPurchaseState) {
    NamiPurchaseState.PURCHASED -> {
      // check external id on success
      println("external id: " + ex_id)
    }
  }
}
useEffect(() => {
  eventEmitter.addListener('PurchasesChanged', onPurchasesChanged);
}
          
const onPurchasesChanged = (event) => {  
  if (event.purchaseState == "PURCHASED") {
    NativeModules.NamiBridge.getExternalIdentifier( (externalId) => { 
        console.log("external id: " + externalId)
    });
  } else if (event.purchaseState == "PENDING") {
    NativeModules.NamiBridge.getExternalIdentifier( (externalId) => { 
        console.log("external id: " + externalId)
    });
  }
}

Look at the Network Traffic from the Device

For Apple apps you can do this with Charles. For Android, you can use the Profiler in Android Studio.

When you call setExternalIdentifier there will be an API call made to https://app.namiml.com (or your Nami API Host if you have dedicated or self-hosted infrastructure) that updates the external identifier and a separate API call made at the time a transaction occurs.

You can type the API host into the Filter box in Charles to just see calls to the Nami API.

You can review the order of these API calls to see if the transaction call is being made before the set external identifier call, which would indicate that the purchase is likely being assigned initially to an anonymous user.

You are looking for 2 calls in particular. The setting of the external identifier will happen in a PUT /sdk/v2/platform/{id}/device/{id} call.

The purchase then happens in the POST /sdk/v2/platform/{id}/device/{id}/transaction call.

Example SDK API calls to the Nami platform in Charles.Example SDK API calls to the Nami platform in Charles.

Example SDK API calls to the Nami platform in Charles.

Note that there are multiple PUT device calls that the SDK can make, so please check to make sure that the body of the PUT call has data about the external identifier. See the example below.

Additionally, you may wish to look for a call to the purchase validation endpoint. This endpoint varies by platform. If API calls to the purchase validation endpoint are not happening or delayed, this will affect both the time at which a purchase is granted and potentially when a Server notification is linked to a device, which could result in a transfer.

  • Apple POST /sdk/v2/platform/{id}/device/{id}/apple-itunes-receipt/

📘

Apple Sandbox Behavior

Apple's purchase sandbox is not as reliable as their production environment. If a purchase is not completing properly or you are not seeing a call to the apple-itunes-receipt endpoint at the start of an app session, this may indicate that StoreKit is not properly processing the purchase events or they are running slow.


Did this page help you?