Android Purchase Connector

License: MIT Release Artifacts

🛠 In order for us to provide optimal support, we would kindly ask you to submit any issues to support@appsflyer.com

When submitting an issue please specify your AppsFlyer sign-up (account) email , your app ID , production steps, logs, code snippets and any additional relevant information.

Table Of Content

  • Android AppsFlyer SDK 6.12.2 and above

Purchase Connector v2.0.0 (and above) can only be used with SDK v6.12.2 (and above), as this is the setup that supports Billing Library v5.x.x and V6.x.x.

Using Purchase Connector v2.x.x with an older SDK version will cause the server to reject the Purchase requests.

Purcahse Connector Version Supported Billing Library Versions Supported AppsFlyer SDK Versions
v2.0.0 v5.x.x v6.12.2 and above
v2.0.1 v5.x.x - v6.x.x v6.12.2 and above
  1. Add to your build.gradle file:

    implementation 'com.appsflyer:purchase-connector:2.0.1'
    implementation 'com.android.billingclient:billing:$play_billing_version'
    

    where play_billing_version is 5.x.x or 6.x.x.

  2. If you are using ProGuard, add following keep rules to your proguard-rules.pro file:

    -keep class com.appsflyer.** { *; }
    -keep class kotlin.jvm.internal.Intrinsics{ *; }
    -keep class kotlin.collections.**{ *; }
    

Create an instance of this Connector to configure (in the following steps) for observing and validating transactions in your app.
Make sure to save a reference to the built object. If the object is not saved, it could lead to unexpected behavior and memory leaks.

  • Kotlin
        // init
        val builder = PurchaseClient.Builder(this, Store.GOOGLE)
        // Make sure to keep this instance
        val afPurchaseClient = builder.build()
  • Java
        // init
        PurchaseClient.Builder builder = new PurchaseClient.Builder(context, Store.GOOGLE);
        // Make sure to keep this instance
        PurchaseClient purchaseClient = builder.build();

Start the SDK instance to observe transactions.

⚠️ Please Note

This should be called right after calling the Android SDK's start method. Calling startObservingTransactions activates a listener that automatically observes new billing transactions. This includes new and existing subscriptions and new in app purchases. The best practice is to activate the listener as early as possible, preferably in the Application class.

  • Kotlin
        // start
        afPurchaseClient.startObservingTransactions()
  • Java
        // start
        afPurchaseClient.startObservingTransactions();

Stop the SDK instance from observing transactions.
⚠️ Please Note

This should be called if you would like to stop the Connector from listening to billing transactions. This removes the listener and stops observing new transactions. An example for using this API is if the app wishes to stop sending data to AppsFlyer due to changes in the user's consent (opt-out from data sharing). Otherwise, there is no reason to call this method. If you do decide to use it, it should be called right before calling the Android SDK's stop API

  • Kotlin
        // start
        afPurchaseClient.stopObservingTransactions()
  • Java
        // start
        afPurchaseClient.stopObservingTransactions();

Enables automatic logging of subscription events.
Set true to enable, false to disable.
If this API is not used, by default, the connector will not record Subscriptions.

  • Kotlin
        builder.logSubscriptions(true)
  • Java
        builder.logSubscriptions(true);

Enables automatic logging of In-App purchase events
Set true to enable, false to disable.
If this API is not used, by default, the connector will not record In App Purchases.

  • Kotlin
        builder.autoLogInApps(true)
  • Java
        builder.autoLogInApps(true);

Purchase Event Data source listener. Invoked before sending data to AppsFlyer servers to let the developer add extra parameters to the payload.

  • Kotlin
        builder.setSubscriptionPurchaseEventDataSource(object : PurchaseClient.SubscriptionPurchaseEventDataSource{
            override fun onNewPurchases(purchaseEvents: List<SubscriptionPurchaseEvent>): Map<String, Any> {
                return mapOf("some key" to "some value")
            }
        })

        // or use lambda
        builder.setSubscriptionPurchaseEventDataSource {
            mapOf(
                "some key" to "some value",
                "another key" to it.size
            )
        }
  • Java
        builder.setSubscriptionPurchaseEventDataSource(new PurchaseClient.SubscriptionPurchaseEventDataSource() {
            @NonNull
            @Override
            public Map<String, Object> onNewPurchases(@NonNull List<? extends SubscriptionPurchaseEvent> purchaseEvents) {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("some key", "value");
                return map;
            }
        });

        // or use lambda 
        builder.setSubscriptionPurchaseEventDataSource(purchaseEvents -> {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("some key", "value");
            return map;
        });
  • Kotlin
        builder.setInAppPurchaseEventDataSource(object :
            PurchaseClient.InAppPurchaseEventDataSource {
            override fun onNewPurchases(purchaseEvents: List<InAppPurchaseEvent>): Map<String, Any> {
                return mapOf(
                    "some key" to "some value",
                    "another key" to purchaseEvents.size
                )
            }
        })

        // or use lambda
        builder.setInAppPurchaseEventDataSource { 
            mapOf(
                "some key" to "some value",
                "another key" to it.size
            )
        }
  • Java
        builder.setInAppPurchaseEventDataSource(new PurchaseClient.InAppPurchaseEventDataSource() {
            @NonNull
            @Override
            public Map<String, Object> onNewPurchases(@NonNull List<? extends InAppPurchaseEvent> purchaseEvents) {
                Map<String, Object> map = new HashMap<String, Object>();
                map.put("some key", "value");
                return map;
            }
        });

        // or use lambda 
        builder.setInAppPurchaseEventDataSource(purchaseEvents -> {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("some key", "value");
            return map;
        });

You can register listeners to get the validation results once getting a response from AppsFlyer servers to let you know if the purchase was validated successfully.

Listener Method Description
onResponse(result: Result?) Invoked when we got 200 OK response from the server (INVALID purchase is considered to be successful response and will be returned to this callback)
onFailure(result: String, error: Throwable?) Invoked when we got some network exception or non 200/OK response from the server.
  • Kotlin
        builder.setSubscriptionValidationResultListener(object :
            PurchaseClient.SubscriptionPurchaseValidationResultListener {
            override fun onResponse(result: Map<String, SubscriptionValidationResult>?) {
                result?.forEach { (k: String, v: SubscriptionValidationResult?) ->
                    if (v.success) {
                        Log.d(TAG, "[PurchaseConnector]: Subscription with ID $k was validated successfully")
                        val subscriptionPurchase = v.subscriptionPurchase
                        Log.d(TAG, subscriptionPurchase.toString())
                    } else {
                        Log.d(TAG, "[PurchaseConnector]: Subscription with ID $k wasn't validated successfully")
                        val failureData = v.failureData
                        Log.d(TAG, failureData.toString())
                    }
                }
            }

            override fun onFailure(result: String, error: Throwable?) {
                Log.d(TAG, "[PurchaseConnector]: Validation fail: $result")
                error?.printStackTrace()
            }
        })
  • Java
        builder.setSubscriptionValidationResultListener(new PurchaseClient.SubscriptionPurchaseValidationResultListener() {
            @Override
            public void onResponse(@Nullable Map<String, ? extends SubscriptionValidationResult> result) {
                if (result == null) {
                    return;
                }
                result.forEach((k, v) -> {
                    if (v.getSuccess()) {
                        Log.d(TAG, "[PurchaseConnector]: Subscription with ID " + k + " was validated successfully");
                        SubscriptionPurchase subscriptionPurchase = v.getSubscriptionPurchase();
                        Log.d(TAG, subscriptionPurchase.toString());
                    } else {
                        Log.d(TAG, "[PurchaseConnector]: Subscription with ID " + k + " wasn't validated successfully");
                        ValidationFailureData failureData = v.getFailureData();
                        Log.d(TAG, failureData.toString());
                    }
                });
            }

            @Override
            public void onFailure(@NonNull String result, @Nullable Throwable error) {
                Log.d(TAG, "[PurchaseConnector]: Validation fail: " + result);
                if (error != null) {
                    error.printStackTrace();
                }
            }
        });
  • Kotlin
        builder.setInAppValidationResultListener(object :
            PurchaseClient.InAppPurchaseValidationResultListener {
            override fun onResponse(result: Map<String, InAppPurchaseValidationResult>?) {
                result?.forEach { (k: String, v: InAppPurchaseValidationResult?) ->
                    if (v.success) {
                        Log.d(TAG, "[PurchaseConnector]:  Product with Purchase Token$k was validated successfully")
                        val productPurchase = v.productPurchase
                        Log.d(TAG, productPurchase.toString())
                    } else {
                        Log.d(TAG, "[PurchaseConnector]:  Product with Purchase Token $k wasn't validated successfully")
                        val failureData = v.failureData
                        Log.d(TAG, failureData.toString())
                    }
                }
            }

            override fun onFailure(result: String, error: Throwable?) {
                Log.d(TAG, "[PurchaseConnector]: Validation fail: $result")
                error?.printStackTrace()
            }
        })
  • Java
        builder.setInAppValidationResultListener(new PurchaseClient.InAppPurchaseValidationResultListener() {
            @Override
            public void onResponse(@Nullable Map<String, ? extends InAppPurchaseValidationResult> result) {
                if (result == null) {
                    return;
                }
                result.forEach((k, v) -> {
                    if (v.getSuccess()) {
                        Log.d(TAG, "[PurchaseConnector]: Product with Purchase Token " + k + " was validated successfully");
                        ProductPurchase productPurchase = v.getProductPurchase();
                        Log.d(TAG, productPurchase.toString());
                    } else {
                        Log.d(TAG, "[PurchaseConnector]: Subscription with Purchase Token " + k + " wasn't validated successfully");
                        ValidationFailureData failureData = v.getFailureData();
                        Log.d(TAG, failureData.toString());
                    }
                });
            }

            @Override
            public void onFailure(@NonNull String result, @Nullable Throwable error) {
                Log.d(TAG, "[PurchaseConnector]: Validation fail: " + result);
                if (error != null) {
                    error.printStackTrace();
                }
            }
        });

You can select which environment will be used for validation production or sandbox (production by default). The sandbox environment should be used while testing your Google Play Billing Library integration.
To set the environment to sandbox, call the following builder method with true value. Make sure to set the environment to production before uploading your app to the plat store (call the method with false or completely remove this call).

  • Kotlin
        // sandbox environment
        builder.setSandbox(true)
        // production environment
        builder.setSandbox(false)
  • Java
        // sandbox environment
        builder.setSandbox(true);
        // production environment
        builder.setSandbox(false);
  • Kotlin
    override fun onCreate() {
        super.onCreate()
        // init and start the native AppsFlyer Core SDK
        AppsFlyerLib.getInstance().apply {
            init("YOUR_DEV_KEY", listener, applicationContext)
            start(applicationContext)
        }
        // init - Make sure to save a reference to the built object. If the object is not saved, 
        // it could lead to unexpected behavior and memory leaks.
        val afPurchaseClient = PurchaseClient.Builder(applicationContext, Store.GOOGLE)
            // Enable Subscriptions auto logging
            .logSubscriptions(true)
            // Enable In Apps auto logging
            .autoLogInApps(true)
            // set production environment
            .setSandbox(false)
            // Subscription Purchase Event Data source listener. Invoked before sending data to AppsFlyer servers
            // to let customer add extra parameters to the payload
            .setSubscriptionPurchaseEventDataSource {
                mapOf(
                    "some key" to "some value",
                    "another key" to it.size
                )
            }
            // In Apps Purchase Event Data source listener. Invoked before sending data to AppsFlyer servers
            // to let customer add extra parameters to the payload
            .setInAppPurchaseEventDataSource {
                mapOf(
                    "some key" to "some value",
                    "another key" to it.size
                )
            }
            // Subscriptions Purchase Validation listener. Invoked after getting response from AppsFlyer servers
            // to let customer know if purchase was validated successfully
            .setSubscriptionValidationResultListener(object :
                PurchaseClient.SubscriptionPurchaseValidationResultListener {
                override fun onResponse(result: Map<String, SubscriptionValidationResult>?) {
                    result?.forEach { (k: String, v: SubscriptionValidationResult?) ->
                        if (v.success) {
                            Log.d(
                                TAG,
                                "[PurchaseConnector]: Subscription with ID $k was validated successfully"
                            )
                            val subscriptionPurchase = v.subscriptionPurchase
                            Log.d(TAG, subscriptionPurchase.toString())
                        } else {
                            Log.d(
                                TAG,
                                "[PurchaseConnector]: Subscription with ID $k wasn't validated successfully"
                            )
                            val failureData = v.failureData
                            Log.d(TAG, failureData.toString())
                        }
                    }
                }

                override fun onFailure(result: String, error: Throwable?) {
                    Log.d(TAG, "[PurchaseConnector]: Validation fail: $result")
                    error?.printStackTrace()
                }
            })
            // In Apps Purchase Validation listener. Invoked after getting response from AppsFlyer servers
            // to let customer know if purchase was validated successfully
            .setInAppValidationResultListener(object :
                PurchaseClient.InAppPurchaseValidationResultListener {
                override fun onResponse(result: Map<String, InAppPurchaseValidationResult>?) {
                    result?.forEach { (k: String, v: InAppPurchaseValidationResult?) ->
                        if (v.success) {
                            Log.d(
                                TAG,
                                "[PurchaseConnector]:  Product with Purchase Token$k was validated successfully"
                            )
                            val productPurchase = v.productPurchase
                            Log.d(TAG, productPurchase.toString())
                        } else {
                            Log.d(
                                TAG,
                                "[PurchaseConnector]:  Product with Purchase Token $k wasn't validated successfully"
                            )
                            val failureData = v.failureData
                            Log.d(TAG, failureData.toString())
                        }
                    }
                }

                override fun onFailure(result: String, error: Throwable?) {
                    Log.d(TAG, "[PurchaseConnector]: Validation fail: $result")
                    error?.printStackTrace()
                }
            })
            // Build the client
            .build()

        // Start the SDK instance to observe transactions.
        afPurchaseClient.startObservingTransactions()
    }
  • Java
    @Override
    public void onCreate() {
        super.onCreate();
        AppsFlyerLib.getInstance().init("YOUR_DEV_KEY", listener, getApplicationContext());
        AppsFlyerLib.getInstance().start(getApplicationContext());
        // init - Make sure to save a reference to the built object. If the object is not saved,
        // it could lead to unexpected behavior and memory leaks.
        PurchaseClient afPurchaseClient = new PurchaseClient.Builder(getApplicationContext(), Store.GOOGLE)
                // Enable Subscriptions auto logging
                .logSubscriptions(true)
                // Enable In Apps auto logging
                .autoLogInApps(true)
                // set production environment
                .setSandbox(false)
                // Subscription Purchase Event Data source listener. Invoked before sending data to AppsFlyer servers
                // to let customer add extra parameters to the payload
                .setSubscriptionPurchaseEventDataSource(purchaseEvents -> {
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("somekey", "value");
                    map.put("type", "Subscription");
                    return map;
                })
                // In Apps Purchase Event Data source listener. Invoked before sending data to AppsFlyer servers
                // to let customer add extra parameters to the payload
                .setInAppPurchaseEventDataSource(purchaseEvents -> {
                    Map<String, Object> map = new HashMap<String, Object>();
                    map.put("somekey", "value");
                    map.put("type", "InApps");
                    return map;
                })
                // Subscriptions Purchase Validation listener. Invoked after getting response from AppsFlyer servers
                // to let customer know if purchase was validated successfully
                .setSubscriptionValidationResultListener(new PurchaseClient.SubscriptionPurchaseValidationResultListener() {
                    @Override
                    public void onResponse(@Nullable Map<String, ? extends SubscriptionValidationResult> result) {
                        if (result == null) {
                            return;
                        }
                        result.forEach((k, v) -> {
                            if (v.getSuccess()) {
                                Log.d(TAG, "[PurchaseConnector]: Subscription with ID " + k + " was validated successfully");
                                SubscriptionPurchase subscriptionPurchase = v.getSubscriptionPurchase();
                                Log.d(TAG, subscriptionPurchase.toString());
                            } else {
                                Log.d(TAG, "[PurchaseConnector]: Subscription with ID " + k + " wasn't validated successfully");
                                ValidationFailureData failureData = v.getFailureData();
                                Log.d(TAG, failureData.toString());
                            }
                        });
                    }

                    @Override
                    public void onFailure(@NonNull String result, @Nullable Throwable error) {
                        Log.d(TAG, "[PurchaseConnector]: Validation fail: " + result);
                        if (error != null) {
                            error.printStackTrace();
                        }
                    }
                })
                // In Apps Purchase Validation listener. Invoked after getting response from AppsFlyer servers
                // to let customer know if purchase was validated successfully
                .setInAppValidationResultListener(new PurchaseClient.InAppPurchaseValidationResultListener() {
                    @Override
                    public void onResponse(@Nullable Map<String, ? extends InAppPurchaseValidationResult> result) {
                        if (result == null) {
                            return;
                        }
                        result.forEach((k, v) -> {
                            if (v.getSuccess()) {
                                Log.d(TAG, "[PurchaseConnector]: Product with Purchase Token " + k + " was validated successfully");
                                ProductPurchase productPurchase = v.getProductPurchase();
                                Log.d(TAG, productPurchase.toString());
                            } else {
                                Log.d(TAG, "[PurchaseConnector]: Subscription with Purchase Token " + k + " wasn't validated successfully");
                                ValidationFailureData failureData = v.getFailureData();
                                Log.d(TAG, failureData.toString());
                            }
                        });
                    }

                    @Override
                    public void onFailure(@NonNull String result, @Nullable Throwable error) {
                        Log.d(TAG, "[PurchaseConnector]: Validation fail: " + result);
                        if (error != null) {
                            error.printStackTrace();
                        }
                    }
                })
                // Build the client
                .build();

        // Start the SDK instance to observe transactions.
        afPurchaseClient.startObservingTransactions();
    }